diff --git a/.github/workflows/Readme.md b/.github/workflows/Readme.md new file mode 100644 index 0000000000..52ce58143f --- /dev/null +++ b/.github/workflows/Readme.md @@ -0,0 +1,133 @@ +# healthtable + +[healthtable](../../Readme.md) - in addition to information about modules, their stability contains the results of CI/CD of the master and alpha branches. + +# for_pr_rust_push.yml + +actions: +- install stable rust +- install nightly rust +- install willbe +- run tests with all features, but only on stable toolchain and in debug optimization mode + +Groups creates by strategy: +```yml +for_pr_rust_push_${{ inputs.module_name }}_${{ github.ref }}_ + ${{ contains( inputs.commit_message, '+test' ) || startsWith( inputs.commit_message, 'merge' ) }} +``` + +inputs.module_name - name of module +github.ref - name of branch +{{ contains( inputs.commit_message, '+test' ) || startsWith( inputs.commit_message, 'merge' ) }} - returns true if commit message contains directive `+test` or starts with `merge` word. + +runs if commit message contains directive `+test` or starts with `merge` word. + +# standard_rust_push.yml + +actions: +- install stable rust +- install nightly rust +- install cargo-udeps +- install cargo-audit +- checks crate with cargo-udeps +- checks crete with cargo-audit +- install willbe +- run tests with all features, with stable and nightly toolchain, with release and debug optimization mode + +Groups creates by strategy: +```yml +standard_rust_push_${{ inputs.module_name }}_${{ github.ref }}_ + ${{ contains( inputs.commit_message, '+test' ) || startsWith( inputs.commit_message, 'merge' ) }} +``` + +inputs.module_name - name of module +github.ref - name of branch +{{ contains( inputs.commit_message, '+test' ) || startsWith( inputs.commit_message, 'merge' ) }} - returns true if commit message contains directive `+test` or starts with `merge` word. + +runs if commit message contains directive `+test` or starts with `merge` word. + +# standard_rust_pool_request.yml + +actions: +- call [for_pr_rust_push.yml](#for_pr_rust_pushyml) for all project. + +# standard_rust_schedule.yml + +actions: +- call [standard_rust_push.yml](#standard_rust_pushyml) for `{module_name}` every day at 1:00 a.m. + +Affects badges in the header of the workspace readme.md file, looks like this [![wTools](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/standard_rust_scheduled.yml?label=master&logo=github&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/standard_rust_scheduled.yml). + +# module_{module_name}_push.yml + +actions: +- call [standard_rust_push.yml](#standard_rust_pushyml) for `{module_name}`. + +Affects badges that are opposite to modules in the **[healthtable](#healthtable)**, as well as badges in the header of the crate readme.md files. + + +# appropriate_branch.yml + +This workflow ensures that pull requests are opened against the correct target branches based on a predefined branching strategy (alpha -> beta -> master). It checks whether the destination branch specified in the pull request matches the expected branch according to the branching strategy. If it doesn't match, the pull request is converted to draft mode, and if it still doesn't match, the workflow fails. + +# appropriate_branch_beta.yml + +This workflow delegates the actual validation and actions to another workflow file (appropriate_branch.yml) located in the Wandalen/wTools repository under .github/workflows directory on the "alpha" branch. It ensures that pull requests targeting the "beta" branch are appropriately validated and processed according to the rules defined in the external workflow file. + +# appropriate_branch_master.yml + +Similar to the previous workflow, this one also delegates the validation and processing of pull requests to an external workflow file (appropriate_branch.yml) located in the Wandalen/wTools repository under the .github/workflows directory on the "alpha" branch. +By specifying the "beta" branch as the source branch and dynamically referencing the base branch of the pull request as the destination branch, this workflow ensures that pull requests targeting the "main" or "master" branches are appropriately validated and processed according to the rules defined in the external workflow file. +This setup promotes consistency and reusability of workflow logic across different branches within the repository, helping to maintain a standardized process for handling pull requests. + +# auto_merge_to_beta.yml + +This workflow automates the process of merging changes from the "alpha" branch into the "beta" branch after ensuring that related workflow runs for modules have completed successfully. +It waits for the completion of workflow runs related to modules and checks their statuses before proceeding with the merge process. +If all checks pass, it merges the changes into the "beta" branch using the provided GitHub token. + +# auto_pr.yml + +This workflow automates the process of opening pull requests between specified source and destination branches. +Upon triggering, it checks out the repository and opens a pull request from the source branch (src_branch) to the destination branch (dst_branch). +The pull request title is automatically generated to indicate that it's an automated pull request forwarding from one branch to another. +If a pull request already exists between the specified branches and PASS_IF_EXISTS is set to true, the action will pass without creating a new pull request. + +# auto_pr_to_alpha.yml + +This workflow automates the process of opening pull requests from any branch except for those explicitly excluded to the "alpha" branch. +It leverages branch filtering to include all branches and exclude specific ones such as master, main, alpha, beta, and any branches containing test or experiment in their names. +When triggered by a push event on a qualifying branch, it calls the external workflow (auto_pr.yml) to handle the process of opening a pull request to the "alpha" branch, passing the source and destination branch information along with the GitHub bot token for authentication. + +# auto_pr_to_beta.yml + +This workflow automates the process of opening pull requests from the "alpha" branch to the "beta" branch. +When triggered by a push event on the "alpha" branch, it calls the external workflow (auto_pr.yml) to handle the process of opening a pull request to the "beta" branch, passing the source and destination branch information along with the GitHub bot token for authentication. + +# auto_pr_to_master.yml + +This workflow automates the process of opening pull requests from the "beta" branch to the "master" branch. +When triggered by a push event on the "beta" branch, it calls the external workflow (auto_pr.yml) to handle the process of opening a pull request to the "master" branch, passing the source and destination branch information along with the GitHub bot token for authentication. + +# runs_clean.yml + +This workflow allows manual triggering to clean up workflow runs in the repository. +It first deletes any runs that have been cancelled or skipped, ensuring that they do not clutter the workflow history. +Then, it deletes runs older than a specified number of days, while ensuring that at least 20 runs are preserved regardless of their age. +By regularly cleaning up older workflow runs, this workflow helps maintain a clean and organized workflow history in the repository. + +# standard_rust_status.yml + +This workflow serves as a status monitor for the completion of specific workflows: "auto_merge_to_beta" and "rust_scheduled." +Upon completion of any of these workflows, it checks the status of their runs. +It employs a matrix strategy to iterate over different workflow files to check their statuses. +If the conclusion of any checked workflow run is "failure," "cancelled," or "skipped," the workflow exits with an error, indicating a problem. + +# status_checks_rules_update.yml + +When a pull request is opened targeting branches "alpha" or "beta": +- If the base branch is "beta": + - It compares the contents of the workflow directories between branches "alpha" and "beta". + - If they are not equal, it triggers an update of branch protection rules for the "beta" branch. +- If the base branch is "alpha": + - It directly triggers an update of branch protection rules for the "alpha" branch with specific required status checks for different contexts. \ No newline at end of file diff --git a/.github/workflows/for_pr_rust_push.yml b/.github/workflows/for_pr_rust_push.yml new file mode 100644 index 0000000000..edbc5aa907 --- /dev/null +++ b/.github/workflows/for_pr_rust_push.yml @@ -0,0 +1,72 @@ + +name : for_pr_push + +on : + + workflow_call : + inputs : + manifest_path : + required : true + type : string + module_name : + required : true + type : string + commit_message : + required : true + type : string + with_smoke : + required : false + type : string + default : true + +concurrency : + + group : for_pr_rust_push_${{ inputs.module_name }}_${{ github.ref }}_ + ${{ contains( inputs.commit_message, '+test' ) || startsWith( inputs.commit_message, 'merge' ) }} + cancel-in-progress : true + +env : + + RUST_BACKTRACE : 1 + CARGO_TERM_COLOR : always + WITH_SMOKE : ${{ inputs.with_smoke }} + +jobs : + + will_test : + if : contains( inputs.commit_message, '+test' ) || contains( inputs.commit_message, 'merge' ) + concurrency : + group : for_pr_rust_push_${{ inputs.module_name }}_${{ github.ref }}_${{ matrix.os }} + cancel-in-progress : true + strategy : + fail-fast : false + matrix : + os : [ ubuntu-latest, windows-latest, macos-latest ] + runs-on : ${{ matrix.os }} + steps : + - name : Install latest stable toolchain + uses : Wandalen/wretry.action/main@master + with : + action : actions-rs/toolchain@v1 + with : | + toolchain : stable + override : true + attempt_limit : 3 + attempt_delay: 10000 + - name: Install latest nightly toolchain + uses: Wandalen/wretry.action/main@master + with: + action: actions-rs/toolchain@v1 + with: | + toolchain : nightly + override : true + attempt_limit: 3 + attempt_delay: 10000 + - uses: actions/checkout@v3 + - name: Install will + run: cargo install --git https://github.com/Wandalen/wTools --branch alpha willbe + - name: Set MANIFEST_ROOT_PATH + id: rootpath + run: echo "::set-output name=path::$(dirname ${{ inputs.manifest_path }})" + - name: Run tests with each feature + run: will .test ${{ steps.rootpath.outputs.path }}/ dry:0 exclude:'' with_all_features:1 with_debug:1 with_nightly:0 with_none_features:1 with_release:0 with_stable:1 \ No newline at end of file diff --git a/.github/workflows/module_program_tools_push.yml b/.github/workflows/module_program_tools_push.yml new file mode 100644 index 0000000000..5b3e79ed27 --- /dev/null +++ b/.github/workflows/module_program_tools_push.yml @@ -0,0 +1,23 @@ +name : program_tools + +on : + push : + branches : + - 'alpha' + - 'beta' + - 'master' + + +env : + CARGO_TERM_COLOR : always + +jobs : + + # program_tools + + test : + uses : Wandalen/wTools/.github/workflows/standard_rust_push.yml@alpha + with : + manifest_path : 'module/core/program_tools/Cargo.toml' + module_name : 'program_tools' + commit_message : ${{ github.event.head_commit.message }} diff --git a/.github/workflows/module_rustql_push.yml b/.github/workflows/module_rustql_push.yml new file mode 100644 index 0000000000..16ca176024 --- /dev/null +++ b/.github/workflows/module_rustql_push.yml @@ -0,0 +1,23 @@ +name : rustql + +on : + push : + branches : + - 'alpha' + - 'beta' + - 'master' + + +env : + CARGO_TERM_COLOR : always + +jobs : + + # rustql + + test : + uses : Wandalen/wTools/.github/workflows/standard_rust_push.yml@alpha + with : + manifest_path : 'module/blank/rustql/Cargo.toml' + module_name : 'rustql' + commit_message : ${{ github.event.head_commit.message }} diff --git a/.github/workflows/standard_rust_pull_request.yml b/.github/workflows/standard_rust_pull_request.yml index 65b3547bfd..f71b959fae 100644 --- a/.github/workflows/standard_rust_pull_request.yml +++ b/.github/workflows/standard_rust_pull_request.yml @@ -43,8 +43,9 @@ jobs : tested : needs: check if : ${{ needs.check.outputs.should_run == 'true' }} - uses : Wandalen/wTools/.github/workflows/standard_rust_push.yml@alpha + uses : Wandalen/wTools/.github/workflows/for_pr_rust_push.yml@alpha with : manifest_path : './Cargo.toml' module_name : ${{ github.event.base.ref }}_${{ github.event.number }} commit_message : "+test_${{ github.event.base.ref }}_${{ github.event.number }}" + with_smoke : false diff --git a/.github/workflows/standard_rust_push.yml b/.github/workflows/standard_rust_push.yml index 5e95425a98..1812c69512 100644 --- a/.github/workflows/standard_rust_push.yml +++ b/.github/workflows/standard_rust_push.yml @@ -22,8 +22,7 @@ on : concurrency : group : standard_rust_push_${{ inputs.module_name }}_${{ github.ref }}_ - ${{ contains( inputs.commit_message, '+test' ) || startsWith( inputs.commit_message, 'Merge' ) || contains( inputs.commit_message, inputs.module_name ) }}_ - ${{ !contains( inputs.commit_message, '!only_js' )}} + ${{ contains( inputs.commit_message, '+test' ) || startsWith( inputs.commit_message, 'merge' ) }} cancel-in-progress : true env : diff --git a/Cargo.toml b/Cargo.toml index 132c5b038f..4bfc9102a5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,8 +15,8 @@ exclude = [ [workspace.metadata] branches = [ "master", "alpha" ] -master_branch = "alpha" -project_name = "wtools" +master_branch = "master" +workspace_name = "wTools" repo_url = "https://github.com/Wandalen/wTools" discord_url = "https://discord.gg/m3YfbXpUUY" @@ -92,7 +92,7 @@ default-features = false # path = "module/core/type_constructor_derive_pair_meta" [workspace.dependencies.interval_adapter] -version = "~0.19.0" +version = "~0.21.0" path = "module/core/interval_adapter" default-features = false features = [ "enabled" ] @@ -104,7 +104,7 @@ default-features = false features = [ "enabled" ] [workspace.dependencies.collection_tools] -version = "~0.4.0" +version = "~0.8.0" path = "module/core/collection_tools" default-features = false @@ -112,13 +112,13 @@ default-features = false ## derive [workspace.dependencies.derive_tools] -version = "~0.21.0" +version = "~0.23.0" path = "module/core/derive_tools" default-features = false features = [ "enabled" ] [workspace.dependencies.derive_tools_meta] -version = "~0.18.0" +version = "~0.22.0" path = "module/core/derive_tools_meta" default-features = false features = [ "enabled" ] @@ -146,19 +146,19 @@ path = "module/alias/fundamental_data_type" default-features = false [workspace.dependencies.variadic_from] -version = "~0.14.0" +version = "~0.18.0" path = "module/core/variadic_from" default-features = false features = [ "enabled" ] [workspace.dependencies.clone_dyn] -version = "~0.16.0" +version = "~0.17.0" path = "module/core/clone_dyn" default-features = false features = [ "enabled" ] [workspace.dependencies.clone_dyn_meta] -version = "~0.16.0" +version = "~0.17.0" path = "module/core/clone_dyn_meta" features = [ "enabled" ] @@ -182,7 +182,7 @@ default-features = false ## iter [workspace.dependencies.iter_tools] -version = "~0.16.0" +version = "~0.17.0" path = "module/core/iter_tools" default-features = false @@ -200,20 +200,25 @@ path = "module/core/for_each" default-features = false [workspace.dependencies.former] -version = "~1.0.0" +version = "~2.1.0" path = "module/core/former" default-features = false [workspace.dependencies.former_stable] package = "former" -version = "=0.15.0" +version = "=2.0.0" default-features = false [workspace.dependencies.former_meta] -version = "~1.0.0" +version = "~2.1.0" path = "module/core/former_meta" default-features = false +[workspace.dependencies.former_types] +version = "~2.2.0" +path = "module/core/former_types" +default-features = false + [workspace.dependencies.impls_index] version = "~0.7.0" path = "module/core/impls_index" @@ -224,12 +229,12 @@ version = "~0.7.0" path = "module/core/impls_index_meta" [workspace.dependencies.mod_interface] -version = "~0.18.0" +version = "~0.20.0" path = "module/core/mod_interface" default-features = false [workspace.dependencies.mod_interface_meta] -version = "~0.18.0" +version = "~0.20.0" path = "module/core/mod_interface_meta" default-features = false @@ -255,7 +260,7 @@ default-features = false ## macro tools [workspace.dependencies.macro_tools] -version = "~0.24.0" +version = "~0.27.0" path = "module/core/macro_tools" default-features = false @@ -309,7 +314,7 @@ default-features = false ## error [workspace.dependencies.error_tools] -version = "~0.13.0" +version = "~0.14.0" path = "module/core/error_tools" default-features = false @@ -321,7 +326,7 @@ path = "module/alias/werror" ## string tools [workspace.dependencies.strs_tools] -version = "~0.11.0" +version = "~0.13.0" path = "module/core/strs_tools" default-features = false @@ -343,7 +348,7 @@ path = "module/alias/file_tools" default-features = false [workspace.dependencies.proper_path_tools] -version = "~0.4.0" +version = "~0.6.0" path = "module/core/proper_path_tools" default-features = false @@ -351,7 +356,7 @@ default-features = false ## process tools [workspace.dependencies.process_tools] -version = "~0.3.0" +version = "~0.5.0" path = "module/core/process_tools" default-features = false @@ -398,7 +403,7 @@ default-features = false ## ca [workspace.dependencies.wca] -version = "~0.15.0" +version = "~0.17.0" path = "module/move/wca" @@ -412,7 +417,7 @@ path = "module/move/wcensor" ## willbe [workspace.dependencies.willbe] -version = "~0.9.0" +version = "~0.11.0" path = "module/move/willbe" @@ -452,7 +457,7 @@ version = "~0.5.0" path = "module/move/deterministic_rand" [workspace.dependencies.crates_tools] -version = "~0.8.0" +version = "~0.10.0" path = "module/move/crates_tools" diff --git a/Readme.md b/Readme.md index 68922b6d86..d372d888da 100644 --- a/Readme.md +++ b/Readme.md @@ -2,13 +2,11 @@ ![wTools](./asset/img/logo_v3_trans_wide.png) - - -[![alpha](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/auto_merge_to_beta.yml?label=alpha&logo=github&branch=alpha)](https://github.com/Wandalen/wTools/blob/master/.github/workflows/auto_merge_to_beta.yml) + +[![wTools](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/standard_rust_scheduled.yml?label=wTools&logo=github&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/standard_rust_scheduled.yml) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) [![Open in Gitpod](https://raster.shields.io/static/v1?label=try&message=online&color=eee&logo=gitpod&logoColor=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=sample%2Frust%2Fwtools_trivial_sample%2Fsrc%2Fmain.rs,RUN_POSTFIX=--example%20wtools_trivial_sample/https://github.com/Wandalen/wTools) -[![docs.rs](https://raster.shields.io/static/v1?label=docs&message=online&color=eee&logo=docsdotrs&logoColor=eee)](https://docs.rs/wtools) - +[![docs.rs](https://raster.shields.io/static/v1?label=docs&message=online&color=eee&logo=docsdotrs&logoColor=eee)](https://docs.rs/wTools) Collection of general purpose tools for solving problems. Fundamentally extend the language without spoiling, so may be used solely or in conjunction with another module of such kind. @@ -24,35 +22,37 @@ Collection of general purpose tools for solving problems. Fundamentally extend t | [clone_dyn_meta](module/core/clone_dyn_meta) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_clone_dyn_meta_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_clone_dyn_meta_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_clone_dyn_meta_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_clone_dyn_meta_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/clone_dyn_meta) | | | [derive_tools_meta](module/core/derive_tools_meta) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_derive_tools_meta_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_derive_tools_meta_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_derive_tools_meta_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_derive_tools_meta_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/derive_tools_meta) | | | [clone_dyn](module/core/clone_dyn) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_clone_dyn_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_clone_dyn_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_clone_dyn_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_clone_dyn_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/clone_dyn) | [![Open in Gitpod](https://raster.shields.io/static/v1?label=&message=try&color=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fcore%2Fclone_dyn%2Fexamples%2Fclone_dyn_trivial.rs,RUN_POSTFIX=--example%20clone_dyn_trivial/https://github.com/Wandalen/wTools) | +| [collection_tools](module/core/collection_tools) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_collection_tools_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_collection_tools_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_collection_tools_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_collection_tools_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/collection_tools) | [![Open in Gitpod](https://raster.shields.io/static/v1?label=&message=try&color=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fcore%2Fcollection_tools%2Fexamples%2Fcollection_tools_trivial.rs,RUN_POSTFIX=--example%20collection_tools_trivial/https://github.com/Wandalen/wTools) | | [variadic_from](module/core/variadic_from) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_variadic_from_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_variadic_from_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_variadic_from_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_variadic_from_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/variadic_from) | [![Open in Gitpod](https://raster.shields.io/static/v1?label=&message=try&color=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fcore%2Fvariadic_from%2Fexamples%2Fvariadic_from_trivial.rs,RUN_POSTFIX=--example%20variadic_from_trivial/https://github.com/Wandalen/wTools) | | [derive_tools](module/core/derive_tools) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_derive_tools_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_derive_tools_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_derive_tools_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_derive_tools_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/derive_tools) | [![Open in Gitpod](https://raster.shields.io/static/v1?label=&message=try&color=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fcore%2Fderive_tools%2Fexamples%2Fderive_tools_trivial.rs,RUN_POSTFIX=--example%20derive_tools_trivial/https://github.com/Wandalen/wTools) | -| [mod_interface_meta](module/core/mod_interface_meta) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_mod_interface_meta_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_mod_interface_meta_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_mod_interface_meta_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_mod_interface_meta_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/mod_interface_meta) | | -| [collection_tools](module/core/collection_tools) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_collection_tools_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_collection_tools_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_collection_tools_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_collection_tools_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/collection_tools) | [![Open in Gitpod](https://raster.shields.io/static/v1?label=&message=try&color=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fcore%2Fcollection_tools%2Fexamples%2Fcollection_tools_trivial.rs,RUN_POSTFIX=--example%20collection_tools_trivial/https://github.com/Wandalen/wTools) | +| [former_types](module/core/former_types) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_former_types_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_former_types_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_former_types_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_former_types_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/former_types) | [![Open in Gitpod](https://raster.shields.io/static/v1?label=&message=try&color=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fcore%2Fformer_types%2Fexamples%2Fformer_types_trivial.rs,RUN_POSTFIX=--example%20former_types_trivial/https://github.com/Wandalen/wTools) | | [former_meta](module/core/former_meta) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_former_meta_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_former_meta_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_former_meta_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_former_meta_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/former_meta) | | | [impls_index_meta](module/core/impls_index_meta) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_impls_index_meta_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_impls_index_meta_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_impls_index_meta_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_impls_index_meta_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/impls_index_meta) | | -| [mod_interface](module/core/mod_interface) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_mod_interface_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_mod_interface_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_mod_interface_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_mod_interface_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/mod_interface) | | -| [error_tools](module/core/error_tools) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_error_tools_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_error_tools_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_error_tools_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_error_tools_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/error_tools) | [![Open in Gitpod](https://raster.shields.io/static/v1?label=&message=try&color=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fcore%2Ferror_tools%2Fexamples%2Ferror_tools_trivial.rs,RUN_POSTFIX=--example%20error_tools_trivial/https://github.com/Wandalen/wTools) | -| [for_each](module/core/for_each) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_for_each_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_for_each_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_for_each_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_for_each_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/for_each) | [![Open in Gitpod](https://raster.shields.io/static/v1?label=&message=try&color=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fcore%2Ffor_each%2Fexamples%2Ffor_each_map_style_sample.rs,RUN_POSTFIX=--example%20for_each_map_style_sample/https://github.com/Wandalen/wTools) | +| [mod_interface_meta](module/core/mod_interface_meta) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_mod_interface_meta_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_mod_interface_meta_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_mod_interface_meta_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_mod_interface_meta_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/mod_interface_meta) | | +| [for_each](module/core/for_each) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_for_each_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_for_each_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_for_each_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_for_each_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/for_each) | [![Open in Gitpod](https://raster.shields.io/static/v1?label=&message=try&color=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fcore%2Ffor_each%2Fexamples%2Ffor_each_trivial.rs,RUN_POSTFIX=--example%20for_each_trivial/https://github.com/Wandalen/wTools) | | [former](module/core/former) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_former_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_former_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_former_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_former_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/former) | [![Open in Gitpod](https://raster.shields.io/static/v1?label=&message=try&color=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fcore%2Fformer%2Fexamples%2Fformer_trivial.rs,RUN_POSTFIX=--example%20former_trivial/https://github.com/Wandalen/wTools) | -| [implements](module/core/implements) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_implements_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_implements_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_implements_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_implements_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/implements) | [![Open in Gitpod](https://raster.shields.io/static/v1?label=&message=try&color=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fcore%2Fimplements%2Fexamples%2Fimplements_trivial_sample.rs,RUN_POSTFIX=--example%20implements_trivial_sample/https://github.com/Wandalen/wTools) | -| [impls_index](module/core/impls_index) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_impls_index_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_impls_index_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_impls_index_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_impls_index_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/impls_index) | | +| [implements](module/core/implements) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_implements_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_implements_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_implements_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_implements_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/implements) | [![Open in Gitpod](https://raster.shields.io/static/v1?label=&message=try&color=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fcore%2Fimplements%2Fexamples%2Fimplements_trivial.rs,RUN_POSTFIX=--example%20implements_trivial/https://github.com/Wandalen/wTools) | +| [impls_index](module/core/impls_index) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_impls_index_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_impls_index_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_impls_index_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_impls_index_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/impls_index) | [![Open in Gitpod](https://raster.shields.io/static/v1?label=&message=try&color=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fcore%2Fimpls_index%2Fexamples%2Fimpls_index_trivial.rs,RUN_POSTFIX=--example%20impls_index_trivial/https://github.com/Wandalen/wTools) | | [inspect_type](module/core/inspect_type) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_inspect_type_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_inspect_type_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_inspect_type_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_inspect_type_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/inspect_type) | [![Open in Gitpod](https://raster.shields.io/static/v1?label=&message=try&color=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fcore%2Finspect_type%2Fexamples%2Finspect_type_trivial.rs,RUN_POSTFIX=--example%20inspect_type_trivial/https://github.com/Wandalen/wTools) | -| [is_slice](module/core/is_slice) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_is_slice_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_is_slice_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_is_slice_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_is_slice_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/is_slice) | [![Open in Gitpod](https://raster.shields.io/static/v1?label=&message=try&color=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fcore%2Fis_slice%2Fexamples%2Fis_slice_trivial_sample.rs,RUN_POSTFIX=--example%20is_slice_trivial_sample/https://github.com/Wandalen/wTools) | -| [proper_path_tools](module/core/proper_path_tools) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_proper_path_tools_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_proper_path_tools_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_proper_path_tools_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_proper_path_tools_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/proper_path_tools) | | -| [data_type](module/core/data_type) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_data_type_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_data_type_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_data_type_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_data_type_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/data_type) | | +| [is_slice](module/core/is_slice) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_is_slice_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_is_slice_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_is_slice_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_is_slice_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/is_slice) | [![Open in Gitpod](https://raster.shields.io/static/v1?label=&message=try&color=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fcore%2Fis_slice%2Fexamples%2Fis_slice_trivial.rs,RUN_POSTFIX=--example%20is_slice_trivial/https://github.com/Wandalen/wTools) | +| [mod_interface](module/core/mod_interface) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_mod_interface_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_mod_interface_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_mod_interface_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_mod_interface_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/mod_interface) | | +| [data_type](module/core/data_type) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_data_type_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_data_type_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_data_type_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_data_type_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/data_type) | [![Open in Gitpod](https://raster.shields.io/static/v1?label=&message=try&color=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fcore%2Fdata_type%2Fexamples%2Fdata_type_trivial.rs,RUN_POSTFIX=--example%20data_type_trivial/https://github.com/Wandalen/wTools) | | [diagnostics_tools](module/core/diagnostics_tools) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_diagnostics_tools_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_diagnostics_tools_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_diagnostics_tools_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_diagnostics_tools_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/diagnostics_tools) | [![Open in Gitpod](https://raster.shields.io/static/v1?label=&message=try&color=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fcore%2Fdiagnostics_tools%2Fexamples%2Fdiagnostics_tools_trivial.rs,RUN_POSTFIX=--example%20diagnostics_tools_trivial/https://github.com/Wandalen/wTools) | -| [mem_tools](module/core/mem_tools) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_mem_tools_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_mem_tools_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_mem_tools_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_mem_tools_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/mem_tools) | [![Open in Gitpod](https://raster.shields.io/static/v1?label=&message=try&color=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fcore%2Fmem_tools%2Fexamples%2Fmem_tools_trivial_sample.rs,RUN_POSTFIX=--example%20mem_tools_trivial_sample/https://github.com/Wandalen/wTools) | -| [meta_tools](module/core/meta_tools) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_meta_tools_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_meta_tools_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_meta_tools_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_meta_tools_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/meta_tools) | | -| [process_tools](module/core/process_tools) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_process_tools_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_process_tools_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_process_tools_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_process_tools_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/process_tools) | | +| [error_tools](module/core/error_tools) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_error_tools_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_error_tools_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_error_tools_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_error_tools_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/error_tools) | [![Open in Gitpod](https://raster.shields.io/static/v1?label=&message=try&color=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fcore%2Ferror_tools%2Fexamples%2Ferror_tools_trivial.rs,RUN_POSTFIX=--example%20error_tools_trivial/https://github.com/Wandalen/wTools) | +| [mem_tools](module/core/mem_tools) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_mem_tools_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_mem_tools_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_mem_tools_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_mem_tools_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/mem_tools) | [![Open in Gitpod](https://raster.shields.io/static/v1?label=&message=try&color=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fcore%2Fmem_tools%2Fexamples%2Fmem_tools_trivial.rs,RUN_POSTFIX=--example%20mem_tools_trivial/https://github.com/Wandalen/wTools) | +| [meta_tools](module/core/meta_tools) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_meta_tools_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_meta_tools_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_meta_tools_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_meta_tools_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/meta_tools) | [![Open in Gitpod](https://raster.shields.io/static/v1?label=&message=try&color=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fcore%2Fmeta_tools%2Fexamples%2Fmeta_tools_trivial.rs,RUN_POSTFIX=--example%20meta_tools_trivial/https://github.com/Wandalen/wTools) | +| [proper_path_tools](module/core/proper_path_tools) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_proper_path_tools_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_proper_path_tools_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_proper_path_tools_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_proper_path_tools_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/proper_path_tools) | | | [reflect_tools_meta](module/core/reflect_tools_meta) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_reflect_tools_meta_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_reflect_tools_meta_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_reflect_tools_meta_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_reflect_tools_meta_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/reflect_tools_meta) | | -| [strs_tools](module/core/strs_tools) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_strs_tools_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_strs_tools_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_strs_tools_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_strs_tools_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/strs_tools) | [![Open in Gitpod](https://raster.shields.io/static/v1?label=&message=try&color=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fcore%2Fstrs_tools%2Fexamples%2Fstr_toolst_trivial_sample.rs,RUN_POSTFIX=--example%20str_toolst_trivial_sample/https://github.com/Wandalen/wTools) | -| [time_tools](module/core/time_tools) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_time_tools_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_time_tools_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_time_tools_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_time_tools_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/time_tools) | [![Open in Gitpod](https://raster.shields.io/static/v1?label=&message=try&color=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fcore%2Ftime_tools%2Fexamples%2Ftime_tools_trivial_sample.rs,RUN_POSTFIX=--example%20time_tools_trivial_sample/https://github.com/Wandalen/wTools) | -| [typing_tools](module/core/typing_tools) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_typing_tools_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_typing_tools_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_typing_tools_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_typing_tools_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/typing_tools) | [![Open in Gitpod](https://raster.shields.io/static/v1?label=&message=try&color=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fcore%2Ftyping_tools%2Fexamples%2Ftyping_tools_trivial_sample.rs,RUN_POSTFIX=--example%20typing_tools_trivial_sample/https://github.com/Wandalen/wTools) | +| [strs_tools](module/core/strs_tools) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_strs_tools_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_strs_tools_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_strs_tools_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_strs_tools_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/strs_tools) | [![Open in Gitpod](https://raster.shields.io/static/v1?label=&message=try&color=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fcore%2Fstrs_tools%2Fexamples%2Fstrs_tools_trivial.rs,RUN_POSTFIX=--example%20strs_tools_trivial/https://github.com/Wandalen/wTools) | +| [time_tools](module/core/time_tools) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_time_tools_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_time_tools_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_time_tools_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_time_tools_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/time_tools) | [![Open in Gitpod](https://raster.shields.io/static/v1?label=&message=try&color=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fcore%2Ftime_tools%2Fexamples%2Ftime_tools_trivial.rs,RUN_POSTFIX=--example%20time_tools_trivial/https://github.com/Wandalen/wTools) | +| [typing_tools](module/core/typing_tools) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_typing_tools_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_typing_tools_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_typing_tools_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_typing_tools_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/typing_tools) | [![Open in Gitpod](https://raster.shields.io/static/v1?label=&message=try&color=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fcore%2Ftyping_tools%2Fexamples%2Ftyping_tools_trivial.rs,RUN_POSTFIX=--example%20typing_tools_trivial/https://github.com/Wandalen/wTools) | | [fs_tools](module/core/fs_tools) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_fs_tools_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_fs_tools_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_fs_tools_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_fs_tools_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/fs_tools) | | | [include_md](module/core/include_md) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_include_md_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_include_md_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_include_md_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_include_md_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/include_md) | | +| [process_tools](module/core/process_tools) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_process_tools_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_process_tools_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_process_tools_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_process_tools_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/process_tools) | | +| [program_tools](module/core/program_tools) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_program_tools_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_program_tools_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_program_tools_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_program_tools_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/program_tools) | | | [reflect_tools](module/core/reflect_tools) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_reflect_tools_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_reflect_tools_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_reflect_tools_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_reflect_tools_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/reflect_tools) | [![Open in Gitpod](https://raster.shields.io/static/v1?label=&message=try&color=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fcore%2Freflect_tools%2Fexamples%2Freflect_tools_trivial.rs,RUN_POSTFIX=--example%20reflect_tools_trivial/https://github.com/Wandalen/wTools) | -| [test_tools](module/core/test_tools) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_test_tools_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_test_tools_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_test_tools_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_test_tools_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/test_tools) | | -| [wtools](module/core/wtools) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_wtools_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_wtools_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_wtools_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_wtools_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/wtools) | [![Open in Gitpod](https://raster.shields.io/static/v1?label=&message=try&color=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fcore%2Fwtools%2Fexamples%2Fmain.rs,RUN_POSTFIX=--example%20main/https://github.com/Wandalen/wTools) | +| [test_tools](module/core/test_tools) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_test_tools_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_test_tools_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_test_tools_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_test_tools_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/test_tools) | [![Open in Gitpod](https://raster.shields.io/static/v1?label=&message=try&color=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fcore%2Ftest_tools%2Fexamples%2Ftest_tools_trivial.rs,RUN_POSTFIX=--example%20test_tools_trivial/https://github.com/Wandalen/wTools) | +| [wtools](module/core/wtools) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_wtools_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_wtools_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_wtools_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_wtools_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/wtools) | [![Open in Gitpod](https://raster.shields.io/static/v1?label=&message=try&color=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fcore%2Fwtools%2Fexamples%2Fwtools_trivial.rs,RUN_POSTFIX=--example%20wtools_trivial/https://github.com/Wandalen/wTools) | ### Rust modules to be moved out to other repositories @@ -60,12 +60,12 @@ Collection of general purpose tools for solving problems. Fundamentally extend t | Module | Stability | master | alpha | Docs | Sample | |--------|-----------|--------|--------|:----:|:------:| -| [crates_tools](module/move/crates_tools) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_crates_tools_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_crates_tools_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_crates_tools_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_crates_tools_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/crates_tools) | [![Open in Gitpod](https://raster.shields.io/static/v1?label=&message=try&color=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fmove%2Fcrates_tools%2Fexamples%2Fshow_crate_content.rs,RUN_POSTFIX=--example%20show_crate_content/https://github.com/Wandalen/wTools) | -| [deterministic_rand](module/move/deterministic_rand) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_deterministic_rand_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_deterministic_rand_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_deterministic_rand_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_deterministic_rand_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/deterministic_rand) | [![Open in Gitpod](https://raster.shields.io/static/v1?label=&message=try&color=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fmove%2Fdeterministic_rand%2Fexamples%2Fsample_deterministic_rand_rayon.rs,RUN_POSTFIX=--example%20sample_deterministic_rand_rayon/https://github.com/Wandalen/wTools) | +| [crates_tools](module/move/crates_tools) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_crates_tools_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_crates_tools_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_crates_tools_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_crates_tools_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/crates_tools) | [![Open in Gitpod](https://raster.shields.io/static/v1?label=&message=try&color=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fmove%2Fcrates_tools%2Fexamples%2Fcrates_tools_trivial.rs,RUN_POSTFIX=--example%20crates_tools_trivial/https://github.com/Wandalen/wTools) | +| [deterministic_rand](module/move/deterministic_rand) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_deterministic_rand_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_deterministic_rand_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_deterministic_rand_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_deterministic_rand_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/deterministic_rand) | [![Open in Gitpod](https://raster.shields.io/static/v1?label=&message=try&color=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fmove%2Fdeterministic_rand%2Fexamples%2Fdeterministic_rand_trivial.rs,RUN_POSTFIX=--example%20deterministic_rand_trivial/https://github.com/Wandalen/wTools) | | [wca](module/move/wca) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_wca_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_wca_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_wca_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_wca_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/wca) | [![Open in Gitpod](https://raster.shields.io/static/v1?label=&message=try&color=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fmove%2Fwca%2Fexamples%2Fwca_trivial.rs,RUN_POSTFIX=--example%20wca_trivial/https://github.com/Wandalen/wTools) | | [wplot](module/move/wplot) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_wplot_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_wplot_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_wplot_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_wplot_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/wplot) | | -| [graphs_tools](module/move/graphs_tools) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_graphs_tools_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_graphs_tools_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_graphs_tools_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_graphs_tools_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/graphs_tools) | [![Open in Gitpod](https://raster.shields.io/static/v1?label=&message=try&color=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fmove%2Fgraphs_tools%2Fexamples%2Fgraphs_tools_trivial_sample.rs,RUN_POSTFIX=--example%20graphs_tools_trivial_sample/https://github.com/Wandalen/wTools) | -| [optimization_tools](module/move/optimization_tools) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_optimization_tools_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_optimization_tools_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_optimization_tools_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_optimization_tools_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/optimization_tools) | [![Open in Gitpod](https://raster.shields.io/static/v1?label=&message=try&color=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fmove%2Foptimization_tools%2Fexamples%2Fcustom_problem.rs,RUN_POSTFIX=--example%20custom_problem/https://github.com/Wandalen/wTools) | +| [graphs_tools](module/move/graphs_tools) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_graphs_tools_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_graphs_tools_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_graphs_tools_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_graphs_tools_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/graphs_tools) | [![Open in Gitpod](https://raster.shields.io/static/v1?label=&message=try&color=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fmove%2Fgraphs_tools%2Fexamples%2Fgraphs_tools_trivial.rs,RUN_POSTFIX=--example%20graphs_tools_trivial/https://github.com/Wandalen/wTools) | +| [optimization_tools](module/move/optimization_tools) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_optimization_tools_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_optimization_tools_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_optimization_tools_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_optimization_tools_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/optimization_tools) | [![Open in Gitpod](https://raster.shields.io/static/v1?label=&message=try&color=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fmove%2Foptimization_tools%2Fexamples%2Foptimization_tools_trivial.rs,RUN_POSTFIX=--example%20optimization_tools_trivial/https://github.com/Wandalen/wTools) | | [plot_interface](module/move/plot_interface) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_plot_interface_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_plot_interface_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_plot_interface_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_plot_interface_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/plot_interface) | | | [refiner](module/move/refiner) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_refiner_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_refiner_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_refiner_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_refiner_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/refiner) | | | [sqlx_query](module/move/sqlx_query) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_sqlx_query_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_sqlx_query_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_sqlx_query_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_sqlx_query_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/sqlx_query) | | diff --git a/bash.exe.stackdump b/bash.exe.stackdump new file mode 100644 index 0000000000..ef9b31e0d7 --- /dev/null +++ b/bash.exe.stackdump @@ -0,0 +1,38 @@ +Stack trace: +Frame Function Args +0000005FF160 00021006118E (0002102B5B12, 000210272B3E, 00000000005E, 0000005FACC0) msys-2.0.dll+0x2118E +0000005FF160 0002100469BA (000000000000, 000000000000, 000000000130, 000000001000) msys-2.0.dll+0x69BA +0000005FF160 0002100469F2 (000000000000, 0000000005AF, 00000000005E, 000000000000) msys-2.0.dll+0x69F2 +0000005FF160 0002101791E8 (0002102B5892, 000800000000, 00080000DFB8, 000000000000) msys-2.0.dll+0x1391E8 +0000005FF160 000210183B67 (000000000000, 0002102280C8, 0002102280B0, 0000005FD320) msys-2.0.dll+0x143B67 +0000005FF160 000210046DF4 (00021031C800, 0000005FD320, 000000000000, 000000000000) msys-2.0.dll+0x6DF4 +0000005FF160 00021004850F (00007FFE0384, 000000000000, 000000000000, 000000000000) msys-2.0.dll+0x850F +0000005FF160 00021007251C (000000000000, 000000713234, 000000000000, 000000000000) msys-2.0.dll+0x3251C +0000005FF400 7FFF332D869F (000210040000, 000000000001, 000000000000, 7FFF333E8A70) ntdll.dll+0x2869F +0000005FF400 7FFF3331D03D (0000005FF300, 000000000000, 00000071A078, 000000000001) ntdll.dll+0x6D03D +0000005FF400 7FFF3331CDEE (0000007130D0, 0000005FF400, 000000718450, 000000000000) ntdll.dll+0x6CDEE +0000005FF400 7FFF3331CE60 (7FFF333EB860, 000000000000, 000000213000, 7FFF00000000) ntdll.dll+0x6CE60 +000000000000 7FFF3338EA39 (000000000000, 000000000000, 000000000001, 000000000000) ntdll.dll+0xDEA39 +000000000000 7FFF3337A744 (7FFF332B0000, 000000213050, 0000002157EE, 000000000000) ntdll.dll+0xCA744 +000000000000 7FFF33323EA3 (000000000000, 0000005FFAD0, 000000000000, 000000000000) ntdll.dll+0x73EA3 +000000000000 7FFF33323DCE (000000000000, 0000005FFAD0, 000000000000, 000000000000) ntdll.dll+0x73DCE +End of stack trace +Loaded modules: +000100400000 bash.exe +7FFF332B0000 ntdll.dll +7FFF32910000 KERNEL32.DLL +7FFF307E0000 KERNELBASE.dll +7FFF330C0000 USER32.dll +7FFF307B0000 win32u.dll +000210040000 msys-2.0.dll +7FFF328E0000 GDI32.dll +7FFF305D0000 gdi32full.dll +7FFF30ED0000 msvcp_win.dll +7FFF30C10000 ucrtbase.dll +7FFF31660000 advapi32.dll +7FFF326C0000 msvcrt.dll +7FFF31D90000 sechost.dll +7FFF30D30000 bcrypt.dll +7FFF30FE0000 RPCRT4.dll +7FFF2FE30000 CRYPTBASE.DLL +7FFF30B90000 bcryptPrimitives.dll diff --git a/debug b/debug new file mode 100644 index 0000000000..c233ba713f --- /dev/null +++ b/debug @@ -0,0 +1,3 @@ +[program_tools_v1 0b9968c19] former : property hint - + 1 file changed, 1 insertion(+) +Already up to date. diff --git a/module/alias/proc_macro_tools/Readme.md b/module/alias/proc_macro_tools/Readme.md index bdf7031c4d..4fa511924a 100644 --- a/module/alias/proc_macro_tools/Readme.md +++ b/module/alias/proc_macro_tools/Readme.md @@ -2,7 +2,7 @@ # Module :: proc_macro_tools - [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/module_proc_macro_tools_push.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/module_proc_macro_tools_push.yml) [![docs.rs](https://img.shields.io/docsrs/proc_macro_tools?color=e3e8f0&logo=docs.rs)](https://docs.rs/proc_macro_tools) [![Open in Gitpod](https://raster.shields.io/static/v1?label=try&message=online&color=eee&logo=gitpod&logoColor=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module/alias/proc_macro_tools/examples/proc_macro_tools_trivial.rs,RUN_POSTFIX=--example%20/home/sakapoi/Документи/wTools_fork/module/alias/proc_macro_tools/examples/proc_macro_tools_trivial/https://github.com/Wandalen/wTools) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) + [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/module_proc_macro_tools_push.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/module_proc_macro_tools_push.yml) [![docs.rs](https://img.shields.io/docsrs/proc_macro_tools?color=e3e8f0&logo=docs.rs)](https://docs.rs/proc_macro_tools) [![Open in Gitpod](https://raster.shields.io/static/v1?label=try&message=online&color=eee&logo=gitpod&logoColor=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Falias%2Fproc_macro_tools%2Fexamples%2Fproc_macro_tools_trivial.rs,RUN_POSTFIX=--example%20proc_macro_tools_trivial/https://github.com/Wandalen/wTools) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) Tools for writing procedural macros. diff --git a/module/alias/werror/Readme.md b/module/alias/werror/Readme.md index 5c33e4a695..dc7e2b669a 100644 --- a/module/alias/werror/Readme.md +++ b/module/alias/werror/Readme.md @@ -2,7 +2,7 @@ # Module :: werror - [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/module_werror_push.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/module_werror_push.yml) [![docs.rs](https://img.shields.io/docsrs/werror?color=e3e8f0&logo=docs.rs)](https://docs.rs/werror) [![Open in Gitpod](https://raster.shields.io/static/v1?label=try&message=online&color=eee&logo=gitpod&logoColor=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module/alias/werror/examples/werror_tools_trivial.rs,RUN_POSTFIX=--example%20/home/sakapoi/Документи/wTools_fork/module/alias/werror/examples/werror_tools_trivial/https://github.com/Wandalen/wTools) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) + [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/module_werror_push.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/module_werror_push.yml) [![docs.rs](https://img.shields.io/docsrs/werror?color=e3e8f0&logo=docs.rs)](https://docs.rs/werror) [![Open in Gitpod](https://raster.shields.io/static/v1?label=try&message=online&color=eee&logo=gitpod&logoColor=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Falias%2Fwerror%2Fexamples%2Fwerror_tools_trivial.rs,RUN_POSTFIX=--example%20werror_tools_trivial/https://github.com/Wandalen/wTools) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) Basic exceptions handling mechanism. diff --git a/module/alias/winterval/Readme.md b/module/alias/winterval/Readme.md index 72f6142126..a853161c8c 100644 --- a/module/alias/winterval/Readme.md +++ b/module/alias/winterval/Readme.md @@ -2,7 +2,7 @@ # Module :: winterval - [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/module_winterval_push.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/module_winterval_push.yml) [![docs.rs](https://img.shields.io/docsrs/winterval?color=e3e8f0&logo=docs.rs)](https://docs.rs/winterval) [![Open in Gitpod](https://raster.shields.io/static/v1?label=try&message=online&color=eee&logo=gitpod&logoColor=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module/alias/winterval/examples/winterval_trivial.rs,RUN_POSTFIX=--example%20/home/sakapoi/Документи/wTools_fork/module/alias/winterval/examples/winterval_trivial/https://github.com/Wandalen/wTools) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) + [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/module_winterval_push.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/module_winterval_push.yml) [![docs.rs](https://img.shields.io/docsrs/winterval?color=e3e8f0&logo=docs.rs)](https://docs.rs/winterval) [![Open in Gitpod](https://raster.shields.io/static/v1?label=try&message=online&color=eee&logo=gitpod&logoColor=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Falias%2Fwinterval%2Fexamples%2Fwinterval_trivial.rs,RUN_POSTFIX=--example%20winterval_trivial/https://github.com/Wandalen/wTools) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) Integer interval adapter for both Range and RangeInclusive. diff --git a/module/alias/wstring_tools/Readme.md b/module/alias/wstring_tools/Readme.md index b04fcff48a..ff1e2a4f75 100644 --- a/module/alias/wstring_tools/Readme.md +++ b/module/alias/wstring_tools/Readme.md @@ -2,7 +2,7 @@ # Module :: wstring_tools - [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/module_wstring_tools_push.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/module_wstring_tools_push.yml) [![docs.rs](https://img.shields.io/docsrs/wstring_tools?color=e3e8f0&logo=docs.rs)](https://docs.rs/wstring_tools) [![Open in Gitpod](https://raster.shields.io/static/v1?label=try&message=online&color=eee&logo=gitpod&logoColor=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module/alias/wstring_tools/examples/wstring_toolst_trivial_sample.rs,RUN_POSTFIX=--example%20/home/sakapoi/Документи/wTools_fork/module/alias/wstring_tools/examples/wstring_toolst_trivial_sample/https://github.com/Wandalen/wTools) [![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=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/module_wstring_tools_push.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/module_wstring_tools_push.yml) [![docs.rs](https://img.shields.io/docsrs/wstring_tools?color=e3e8f0&logo=docs.rs)](https://docs.rs/wstring_tools) [![Open in Gitpod](https://raster.shields.io/static/v1?label=try&message=online&color=eee&logo=gitpod&logoColor=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Falias%2Fwstring_tools%2Fexamples%2Fwstring_toolst_trivial_sample.rs,RUN_POSTFIX=--example%20wstring_toolst_trivial_sample/https://github.com/Wandalen/wTools) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) Tools to manipulate strings. diff --git a/module/alias/wtest/Readme.md b/module/alias/wtest/Readme.md index 7b58479b1b..0f3d21c9a8 100644 --- a/module/alias/wtest/Readme.md +++ b/module/alias/wtest/Readme.md @@ -2,7 +2,7 @@ # Module :: wtest - [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/module_wtest_push.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/module_wtest_push.yml) [![docs.rs](https://img.shields.io/docsrs/wtest?color=e3e8f0&logo=docs.rs)](https://docs.rs/wtest) [![Open in Gitpod](https://raster.shields.io/static/v1?label=try&message=online&color=eee&logo=gitpod&logoColor=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module/alias/wtest/examples/wtest_trivial_sample.rs,RUN_POSTFIX=--example%20/home/sakapoi/Документи/wTools_fork/module/alias/wtest/examples/wtest_trivial_sample/https://github.com/Wandalen/wTools) [![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=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/module_wtest_push.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/module_wtest_push.yml) [![docs.rs](https://img.shields.io/docsrs/wtest?color=e3e8f0&logo=docs.rs)](https://docs.rs/wtest) [![Open in Gitpod](https://raster.shields.io/static/v1?label=try&message=online&color=eee&logo=gitpod&logoColor=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Falias%2Fwtest%2Fexamples%2Fwtest_trivial_sample.rs,RUN_POSTFIX=--example%20wtest_trivial_sample/https://github.com/Wandalen/wTools) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) Tools for writing and running tests. diff --git a/module/core/clone_dyn/Cargo.toml b/module/core/clone_dyn/Cargo.toml index a409934586..f71ec38feb 100644 --- a/module/core/clone_dyn/Cargo.toml +++ b/module/core/clone_dyn/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clone_dyn" -version = "0.16.0" +version = "0.17.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", diff --git a/module/core/clone_dyn/Readme.md b/module/core/clone_dyn/Readme.md index 8187c3dc21..4a8efa5ae2 100644 --- a/module/core/clone_dyn/Readme.md +++ b/module/core/clone_dyn/Readme.md @@ -1,7 +1,7 @@ # Module :: clone_dyn - [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/module_clone_dyn_push.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/module_clone_dyn_push.yml) [![docs.rs](https://img.shields.io/docsrs/clone_dyn?color=e3e8f0&logo=docs.rs)](https://docs.rs/clone_dyn) [![Open in Gitpod](https://raster.shields.io/static/v1?label=try&message=online&color=eee&logo=gitpod&logoColor=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module/core/clone_dyn/examples/clone_dyn_trivial.rs,RUN_POSTFIX=--example%20/home/sakapoi/Документи/wTools_fork/module/core/clone_dyn/examples/clone_dyn_trivial/https://github.com/Wandalen/wTools) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) + [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/module_clone_dyn_push.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/module_clone_dyn_push.yml) [![docs.rs](https://img.shields.io/docsrs/clone_dyn?color=e3e8f0&logo=docs.rs)](https://docs.rs/clone_dyn) [![Open in Gitpod](https://raster.shields.io/static/v1?label=try&message=online&color=eee&logo=gitpod&logoColor=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fcore%2Fclone_dyn%2Fexamples%2Fclone_dyn_trivial.rs,RUN_POSTFIX=--example%20clone_dyn_trivial/https://github.com/Wandalen/wTools) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) Derive to clone dyn structures. diff --git a/module/core/clone_dyn_meta/Cargo.toml b/module/core/clone_dyn_meta/Cargo.toml index 0cc0f336f6..e6d9bf304e 100644 --- a/module/core/clone_dyn_meta/Cargo.toml +++ b/module/core/clone_dyn_meta/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clone_dyn_meta" -version = "0.16.0" +version = "0.17.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", diff --git a/module/core/clone_dyn_meta/src/derive.rs b/module/core/clone_dyn_meta/src/derive.rs index 023e852b39..cb006d1a9e 100644 --- a/module/core/clone_dyn_meta/src/derive.rs +++ b/module/core/clone_dyn_meta/src/derive.rs @@ -1,6 +1,6 @@ use macro_tools::prelude::*; -use macro_tools::Result; +use macro_tools::{ Result, generic_params }; // @@ -14,11 +14,10 @@ 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 generic_params = &generics_analyzed.generics.params; - let generics_where = &generics_analyzed.generics.where_clause; - let generics_names = &generics_analyzed.names; + + let generic_params = &item_parsed.generics.params; + let generics_where = &item_parsed.generics.where_clause; + let generics_names : Vec< _ > = generic_params::names( &item_parsed.generics ).collect(); let result = qt! { diff --git a/module/core/collection_tools/Cargo.toml b/module/core/collection_tools/Cargo.toml index 197d4e2ca2..65a81d7cde 100644 --- a/module/core/collection_tools/Cargo.toml +++ b/module/core/collection_tools/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "collection_tools" -version = "0.4.0" +version = "0.8.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", @@ -25,39 +25,40 @@ workspace = true features = [ "full" ] all-features = false - - [features] no_std = [ "test_tools/no_std", ] + use_alloc = [ - "no_std", + "no_std", # qqq : for Anton : why is that better? -- use_alloc means that we do not use std, but alloc and hashbrown "hashbrown", - "test_tools/use_alloc", + # "test_tools/use_alloc", // why is it needed? -- not needed, removed ] default = [ "enabled", + # "reexports", "collection_constructors", "collection_into_constructors", - "collection_std", ] + full = [ "enabled", + # "reexports", "collection_constructors", - "collection_std", + "collection_into_constructors", ] + enabled = [] +# reexports = [] # Collection constructors, like `hmap!{ "key" => "val" }` collection_constructors = [] # Collection constructors, using `into()` under the hood, like `into_hmap!( "key" => "val" )` 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 +# qqq : is this feature used? seems not. if yes, what is it responsible for? discuss -- not needed, removed [dependencies] diff --git a/module/core/collection_tools/Readme.md b/module/core/collection_tools/Readme.md index 07893f301d..1fed2e48ad 100644 --- a/module/core/collection_tools/Readme.md +++ b/module/core/collection_tools/Readme.md @@ -2,7 +2,7 @@ # Module :: collection_tools - [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/module_collection_tools_push.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/module_collection_tools_push.yml) [![docs.rs](https://img.shields.io/docsrs/collection_tools?color=e3e8f0&logo=docs.rs)](https://docs.rs/collection_tools) [![Open in Gitpod](https://raster.shields.io/static/v1?label=try&message=online&color=eee&logo=gitpod&logoColor=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module/core/collection_tools/examples/collection_tools_trivial.rs,RUN_POSTFIX=--example%20/home/sakapoi/Документи/wTools_fork/module/core/collection_tools/examples/collection_tools_trivial/https://github.com/Wandalen/wTools) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) + [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/module_collection_tools_push.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/module_collection_tools_push.yml) [![docs.rs](https://img.shields.io/docsrs/collection_tools?color=e3e8f0&logo=docs.rs)](https://docs.rs/collection_tools) [![Open in Gitpod](https://raster.shields.io/static/v1?label=try&message=online&color=eee&logo=gitpod&logoColor=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fcore%2Fcollection_tools%2Fexamples%2Fcollection_tools_trivial.rs,RUN_POSTFIX=--example%20collection_tools_trivial/https://github.com/Wandalen/wTools) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) Collection of general purpose tools to manipulate collections( containers like Vec/HashMap/HashSet... ). @@ -77,7 +77,7 @@ You can do ```rust -# #[ cfg( all( feature = "enabled", feature = "collection_std" ) ) ] +# #[ cfg( feature = "enabled" ) ] # #[ cfg( any( feature = "use_alloc", not( feature = "no_std" ) ) ) ] # { use collection_tools::HashSet; @@ -120,7 +120,7 @@ While strict macros require you to have all members of the same type, more relax For example: ```rust -# #[ cfg( all( feature = "enabled", feature = "collection_into_constructors" ) ) ] +# #[ cfg( all( feature = "enabled", feature = "collection_into_constructors", any( not( feature = "no_std" ), feature = "use_alloc" ) ) ) ] # { use std::borrow::Cow; let vec : Vec< String > = collection_tools::into_vec!( "&str", "String".to_string(), Cow::from( "Cow" ) ); diff --git a/module/core/collection_tools/src/collections.rs b/module/core/collection_tools/src/collections.rs new file mode 100644 index 0000000000..090cf82267 --- /dev/null +++ b/module/core/collection_tools/src/collections.rs @@ -0,0 +1,32 @@ +/// 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 ) ),* ] ) + ); +} + +/// [BTreeMap] macros +pub mod bmap; +/// [BTreeSet] macros +pub mod bset; +/// [BinaryHeap] macros +pub mod heap; +/// [HashMap] macros +pub mod hmap; +/// [HashSet] macros +pub mod hset; +/// [LinkedList] macros +pub mod list; +/// [Vec] macros +pub mod vec; +/// [VecDeque] macros +pub mod vecd; diff --git a/module/core/collection_tools/src/collections/bmap.rs b/module/core/collection_tools/src/collections/bmap.rs new file mode 100644 index 0000000000..e96f045e84 --- /dev/null +++ b/module/core/collection_tools/src/collections/bmap.rs @@ -0,0 +1,172 @@ +#[ doc( inline ) ] +#[ allow( unused_imports ) ] +pub use alloc::collections::btree_map::*; + +/// Creates a `BTreeMap` from a list of key-value pairs. +/// +/// The `bmap` macro facilitates the convenient creation of a `BTreeMap` with initial elements. +/// +/// # Origin +/// +/// This collection is reexported from `alloc`. +/// +/// # Syntax +/// +/// The macro can be called with a comma-separated list of key-value pairs. A trailing comma is optional. +/// +/// ```rust +/// # use collection_tools::{ BTreeMap, bmap }; +/// // BTreeMap of &str to i32 +/// let map1 = bmap!( "one" => 1, "two" => 2, "three" => 3 ); +/// +/// // BTreeMap of &str to &str +/// let map2 = bmap!{ "name" => "value" }; +/// +/// // With trailing comma +/// let map3 = bmap!( 1 => "one", 2 => "two", 3 => "three", ); +/// ``` +/// +/// # Parameters +/// +/// - `$( $key:expr => $value:expr ),* $( , )?`: A comma-separated list of key-value pairs to insert into the `BTreeMap`. +/// Each key and value can be of any type that implements the `Into< K >` and `Into< V >` traits, where `K` and `V` are the +/// types stored in the `BTreeMap` as keys and values, respectively. +/// +/// # Returns +/// +/// Returns a `BTreeMap` containing all the specified key-value pairs. The map's capacity is +/// automatically determined based on the number of elements provided. +/// +/// # Example +/// +/// Basic usage with string slices and integer values: +/// +/// ```rust +/// # use collection_tools::{ BTreeMap, bmap }; +/// let map = bmap!( "one" => 1, "two" => 2, "three" => 3 ); +/// assert_eq!( map.get( "one" ), Some( &1 ) ); +/// assert_eq!( map.get( "two" ), Some( &2 ) ); +/// assert_eq!( map.get( "three" ), Some( &3 ) ); +/// ``` +/// +/// # Example +/// +/// Creating a `BTreeMap` of integers to string slices from literals: +/// +/// ```rust +/// # use collection_tools::{ BTreeMap, bmap }; +/// let numbers = bmap!( 1 => "one", 2 => "two", 3 => "three" ); +/// assert_eq!( numbers.get( &1 ), Some( &"one" ) ); +/// assert_eq!( numbers.get( &2 ), Some( &"two" ) ); +/// assert_eq!( numbers.get( &3 ), Some( &"three" ) ); +/// ``` +/// +#[ cfg( feature = "collection_constructors" ) ] +#[ macro_export( local_inner_macros ) ] +macro_rules! bmap +{ + ( + $( $key : expr => $value : expr ),* $( , )? + ) + => + {{ + let mut _map = $crate::bmap::BTreeMap::new(); + $( + let _ = _map.insert( $key , $value ); + )* + _map + }}; +} + +/// Creates a `BTreeMap` from a list of key-value pairs. +/// +/// The `into_bmap` macro facilitates the convenient creation of a `BTreeMap` with initial elements. +/// Keys and values passed to the macro are automatically converted into the map's key and value types +/// using `.into()`, enabling the use of literals or values of different, but convertible types. +/// +/// Note: The `into_bmap` macro relies on the `.into()` method to convert each key and value into the target types +/// of the `BTreeMap`. This means that the keys and values must be compatible with the `Into< K >` and `Into< V >` traits +/// for the key type `K` and value type `V` used in the `BTreeMap`. Also, this means that sometimes you must specify the type of collection's items. +/// +/// # Origin +/// +/// This collection is reexported from `alloc`. +/// +/// # Syntax +/// +/// The macro can be called with a comma-separated list of key-value pairs. A trailing comma is optional. +/// +/// ```rust +/// # use collection_tools::{ BTreeMap, into_bmap }; +/// // BTreeMap of &str to i32 +/// let map1 : BTreeMap< &str, i32 > = into_bmap!( "one" => 1, "two" => 2, "three" => 3 ); +/// +/// // BTreeMap of String to String +/// let map2 : BTreeMap< String, String > = into_bmap!{ "name" => "value" }; +/// +/// // With trailing comma +/// let map3 : BTreeMap< i32, &str > = into_bmap!( 1 => "one", 2 => "two", 3 => "three", ); +/// ``` +/// +/// # Parameters +/// +/// - `$( $key:expr => $value:expr ),* $( , )?`: A comma-separated list of key-value pairs to insert into the `BTreeMap`. +/// Each key and value can be of any type that implements the `Into< K >` and `Into< V >` traits, where `K` and `V` are the +/// types stored in the `BTreeMap` as keys and values, respectively. +/// +/// # Returns +/// +/// Returns a `BTreeMap` containing all the specified key-value pairs. The map's capacity is +/// automatically determined based on the number of elements provided. +/// +/// # Example +/// +/// Basic usage with string slices and integer values: +/// +/// ```rust +/// # use collection_tools::{ BTreeMap, into_bmap }; +/// let map : BTreeMap< &str, i32 > = into_bmap!( "one" => 1, "two" => 2, "three" => 3 ); +/// assert_eq!( map.get( "one" ), Some( &1 ) ); +/// assert_eq!( map.get( "two" ), Some( &2 ) ); +/// assert_eq!( map.get( "three" ), Some( &3 ) ); +/// ``` +/// +/// # Example +/// +/// Using with different types that implement `Into< K >` and `Into< V >`: +/// +/// ```rust +/// # use collection_tools::{ BTreeMap, into_bmap }; +/// let months : BTreeMap< String, i32 > = into_bmap!( "January" => 1, "February" => 2, "March" => 3 ); +/// assert_eq!( months.get( &"January".to_string() ), Some( &1 ) ); +/// assert_eq!( months.get( &"February".to_string() ), Some( &2 ) ); +/// ``` +/// +/// # Example +/// +/// Creating a `BTreeMap` of integers to strings from literals: +/// +/// ```rust +/// # use collection_tools::{ BTreeMap, into_bmap }; +/// let numbers : BTreeMap< i32, String > = into_bmap!( 1 => "one", 2 => "two", 3 => "three" ); +/// assert_eq!( numbers.get( &1 ), Some( &"one".to_string() ) ); +/// assert_eq!( numbers.get( &2 ), Some( &"two".to_string() ) ); +/// assert_eq!( numbers.get( &3 ), Some( &"three".to_string() ) ); +/// ``` +/// +#[ cfg( feature = "collection_into_constructors" ) ] +#[ macro_export( local_inner_macros ) ] +macro_rules! into_bmap +{ + ( + $( $key : expr => $value : expr ),* $( , )? + ) + => + {{ + let mut _map = $crate::bmap::BTreeMap::new(); + $( + let _ = _map.insert( Into::into( $key ), Into::into( $value ) ); + )* + _map + }}; +} diff --git a/module/core/collection_tools/src/collections/bset.rs b/module/core/collection_tools/src/collections/bset.rs new file mode 100644 index 0000000000..c0c6d249ed --- /dev/null +++ b/module/core/collection_tools/src/collections/bset.rs @@ -0,0 +1,158 @@ +#[ doc( inline ) ] +#[ allow( unused_imports ) ] +pub use alloc::collections::btree_set::*; + +/// Creates a `BTreeSet` from a list of elements. +/// +/// The `bset` macro allows for convenient creation of a `BTreeSet` with initial elements. +/// +/// # Origin +/// +/// This collection is reexported from `alloc`. +/// +/// # Syntax +/// +/// The macro can be called with a comma-separated list of elements. A trailing comma is optional. +/// +/// ```rust +/// # use collection_tools::{ BTreeSet, bset }; +/// // BTreeSet of &str +/// let set1 = bset!( "a", "b", "c" ); +/// +/// // With trailing comma +/// let set3 = bset!( 1, 2, 3, ); +/// ``` +/// +/// # Parameters +/// +/// - `$( $key:expr ),* $( , )?`: A comma-separated list of elements to insert into the `BTreeSet`. +/// Each element can be of any type that implements the `Into` trait, where `T` is the +/// type stored in the `BTreeSet`. +/// +/// # Returns +/// +/// Returns a `BTreeSet` containing all the specified elements. The capacity of the set is +/// automatically determined based on the number of elements provided. +/// +/// # Example +/// +/// Basic usage with string slices: +/// +/// ```rust +/// # use collection_tools::{ BTreeSet, bset }; +/// let set = bset!( "one", "two", "three" ); +/// assert!( set.contains( "one" ) ); +/// assert!( set.contains( "two" ) ); +/// assert!( set.contains( "three" ) ); +/// assert_eq!( set.len(), 3 ); +/// ``` +/// +#[ cfg( feature = "collection_constructors" ) ] +#[ macro_export( local_inner_macros ) ] +macro_rules! bset +{ + ( + $( $key : expr ),* $( , )? + ) + => + {{ + let mut _set = $crate::bset::BTreeSet::new(); + $( + _set.insert( $key ); + )* + _set + }}; +} + +/// Creates a `BTreeSet` from a list of elements. +/// +/// The `into_bset` macro allows for convenient creation of a `BTreeSet` with initial elements. +/// Elements passed to the macro are automatically converted into the set's element type +/// using `.into()`, facilitating the use of literals or values of different, but convertible types. +/// +/// Note: The `into_bset` macro relies on the `.into()` method to convert each element into the target type +/// of the `BTreeSet`. This means that the elements must be compatible with the `Into` trait for the +/// type `T` used in the `BTreeSet`. Also, this means that sometimes you must specify the type of collection's items. +/// +/// # Origin +/// +/// This collection is reexported from `alloc`. +/// +/// # Syntax +/// +/// The macro can be called with a comma-separated list of elements. A trailing comma is optional. +/// +/// ```rust +/// # use collection_tools::{ BTreeSet, into_bset }; +/// // BTreeSet of &str +/// let set1 : BTreeSet< &str > = into_bset!( "a", "b", "c" ); +/// +/// // BTreeSet of String +/// let set2 : BTreeSet< String > = into_bset!{ "a".to_string(), "b", "c" }; +/// +/// // With trailing comma +/// let set3 : BTreeSet< i32 > = into_bset!( 1, 2, 3, ); +/// ``` +/// +/// # Parameters +/// +/// - `$( $key:expr ),* $( , )?`: A comma-separated list of elements to insert into the `BTreeSet`. +/// Each element can be of any type that implements the `Into` trait, where `T` is the +/// type stored in the `BTreeSet`. +/// +/// # Returns +/// +/// Returns a `BTreeSet` containing all the specified elements. The capacity of the set is +/// automatically determined based on the number of elements provided. +/// +/// # Example +/// +/// Basic usage with string slices: +/// +/// ```rust +/// # use collection_tools::{ BTreeSet, into_bset }; +/// let set : BTreeSet< &str > = into_bset!( "one", "two", "three" ); +/// assert!( set.contains( "one" ) ); +/// assert!( set.contains( "two" ) ); +/// assert!( set.contains( "three" ) ); +/// assert_eq!( set.len(), 3 ); +/// ``` +/// +/// # Example +/// +/// Using with different types that implement `Into`: +/// +/// ```rust +/// # use collection_tools::{ BTreeSet, into_bset }; +/// let numbers : BTreeSet< i32 > = into_bset!( 1, 2, 3 ); +/// assert!( numbers.contains( &1 ) ); +/// assert!( numbers.contains( &2 ) ); +/// assert!( numbers.contains( &3 ) ); +/// ``` +/// +/// # Example +/// +/// Creating a `BTreeSet` of `String` from string literals: +/// +/// ```rust +/// # use collection_tools::{ BTreeSet, into_bset }; +/// let s : BTreeSet< String > = into_bset!{ "value" }; +/// assert!( s.contains( "value" ) ); +/// ``` +/// +#[ cfg( feature = "collection_into_constructors" ) ] +#[ macro_export( local_inner_macros ) ] +macro_rules! into_bset +{ + ( + $( $key : expr ),* $( , )? + ) + => + {{ + let mut _set = $crate::bset::BTreeSet::new(); + $( + _set.insert( Into::into( $key ) ); + )* + _set + }}; +} diff --git a/module/core/collection_tools/src/collections/heap.rs b/module/core/collection_tools/src/collections/heap.rs new file mode 100644 index 0000000000..8d38492497 --- /dev/null +++ b/module/core/collection_tools/src/collections/heap.rs @@ -0,0 +1,155 @@ +#[ doc( inline ) ] +#[ allow( unused_imports ) ] + pub use alloc::collections::binary_heap::*; + +/// Creates a `BinaryHeap` from a list of elements. +/// +/// The `into_heap` macro simplifies the creation of a `BinaryHeap` with initial elements. +/// +/// # Origin +/// +/// This collection is reexported from `alloc`. +/// +/// # Syntax +/// +/// The macro can be called with a comma-separated list of elements. A trailing comma is optional. +/// +/// ```rust +/// # use collection_tools::{ BinaryHeap, heap }; +/// // BinaryHeap of i32 +/// let heap1 = heap!( 3, 1, 4, 1, 5, 9 ); +/// +/// // BinaryHeap of &str +/// let heap2 = heap!{ "pear", "apple", "banana" }; +/// +/// // With trailing comma +/// let heap3 = heap!( 2, 7, 1, 8, ); +/// ``` +/// +/// # Parameters +/// +/// - `$( $key:expr ),* $( , )?`: A comma-separated list of elements to insert into the `BinaryHeap`. +/// Each element can be of any type that implements the `Into` trait, where `T` is the +/// type stored in the `BinaryHeap`. +/// +/// # Returns +/// +/// Returns a `BinaryHeap` containing all the specified elements. The capacity of the heap is +/// automatically determined based on the number of elements provided. +/// +/// # Example +/// +/// Basic usage with integers: +/// +/// ```rust +/// # use collection_tools::{ BinaryHeap, heap }; +/// let heap = heap!( 5, 3, 7, 1 ); +/// assert_eq!( heap.peek(), Some( &7 ) ); // The largest value is at the top of the heap +/// ``` +/// +#[ cfg( feature = "collection_constructors" ) ] +#[ macro_export( local_inner_macros ) ] +macro_rules! heap +{ + ( + $( $key : expr ),* $( , )? + ) + => + {{ + let _cap = count!( @count $( $key ),* ); + let mut _heap = $crate::heap::BinaryHeap::with_capacity( _cap ); + $( + _heap.push( $key ); + )* + _heap + }}; +} + +/// Creates a `BinaryHeap` from a list of elements. +/// +/// The `into_heap` macro simplifies the creation of a `BinaryHeap` with initial elements. +/// Elements passed to the macro are automatically converted into the heap's element type +/// using `.into()`, allowing for the use of literals or values of different, but convertible types. +/// +/// Note: The `into_heap` macro utilizes the `.into()` method to convert each element into the target type +/// of the `BinaryHeap`. This means that the elements must be compatible with the `Into` trait for the +/// type `T` used in the `BinaryHeap`. Also, this means that sometimes you must specify the type of collection's items. +/// +/// # Origin +/// +/// This collection is reexported from `alloc`. +/// +/// # Syntax +/// +/// The macro can be called with a comma-separated list of elements. A trailing comma is optional. +/// +/// ```rust +/// # use collection_tools::{ BinaryHeap, into_heap }; +/// // BinaryHeap of i32 +/// let heap1 : BinaryHeap< i32 > = into_heap!( 3, 1, 4, 1, 5, 9 ); +/// +/// // BinaryHeap of String +/// let heap2 : BinaryHeap< String > = into_heap!{ "pear".to_string(), "apple", "banana" }; +/// +/// // With trailing comma +/// let heap3 : BinaryHeap< i32 > = into_heap!( 2, 7, 1, 8, ); +/// ``` +/// +/// # Parameters +/// +/// - `$( $key:expr ),* $( , )?`: A comma-separated list of elements to insert into the `BinaryHeap`. +/// Each element can be of any type that implements the `Into` trait, where `T` is the +/// type stored in the `BinaryHeap`. +/// +/// # Returns +/// +/// Returns a `BinaryHeap` containing all the specified elements. The capacity of the heap is +/// automatically determined based on the number of elements provided. +/// +/// # Example +/// +/// Basic usage with integers: +/// +/// ```rust +/// # use collection_tools::{ BinaryHeap, into_heap }; +/// let heap : BinaryHeap< i32 > = into_heap!( 5, 3, 7, 1 ); +/// assert_eq!( heap.peek(), Some( &7 ) ); // The largest value is at the top of the heap +/// ``` +/// +/// # Example +/// +/// Using with different types that implement `Into`: +/// +/// ```rust +/// # use collection_tools::{ BinaryHeap, into_heap }; +/// let chars : BinaryHeap< char > = into_heap!( 'a', 'b', 'c' ); +/// assert_eq!( chars.peek(), Some( &'c' ) ); // Characters are ordered by their ASCII value +/// ``` +/// +/// # Example +/// +/// Creating a `BinaryHeap` of `String` from string literals: +/// +/// ```rust +/// # use collection_tools::{ BinaryHeap, into_heap }; +/// let fruits : BinaryHeap< String > = into_heap!{ "cherry", "apple", "banana" }; +/// assert_eq!( fruits.peek(), Some( &"cherry".to_string() ) ); // The lexicographically largest value is at the top +/// ``` +/// +#[ cfg( feature = "collection_into_constructors" ) ] +#[ macro_export( local_inner_macros ) ] +macro_rules! into_heap +{ + ( + $( $key : expr ),* $( , )? + ) + => + {{ + let _cap = count!( @count $( $key ),* ); + let mut _heap = $crate::heap::BinaryHeap::with_capacity( _cap ); + $( + _heap.push( Into::into( $key ) ); + )* + _heap + }}; +} diff --git a/module/core/collection_tools/src/collections/hmap.rs b/module/core/collection_tools/src/collections/hmap.rs new file mode 100644 index 0000000000..eceac4ee9b --- /dev/null +++ b/module/core/collection_tools/src/collections/hmap.rs @@ -0,0 +1,181 @@ +#[ cfg( feature = "use_alloc" ) ] +#[ doc( inline ) ] +#[ allow( unused_imports ) ] +pub use crate::dependency::hashbrown::hash_map::*; +#[ cfg( not( feature = "no_std" ) ) ] +#[ doc( inline ) ] +#[ allow( unused_imports ) ] +pub use std::collections::hash_map::*; + +/// 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: +/// - from `std`, if `no_std` flag if off +/// - from `hashbrown`, if `use_alloc` flag if on +/// +/// # Syntax +/// +/// The macro can be called with a comma-separated list of key-value pairs. A trailing comma is optional. +/// +/// ```rust +/// # use collection_tools::{ HashMap, hmap }; +/// // HashMap of &str to i32 +/// let map1 = hmap!( "one" => 1, "two" => 2, "three" => 3 ); +/// +/// // HashMap of &str to &str +/// let map2 = hmap!{ "name" => "value", "type" => "example" }; +/// +/// // With trailing comma +/// let map3 = hmap!( 1 => "one", 2 => "two", 3 => "three", ); +/// ``` +/// +/// # Parameters +/// +/// - `$( $key:expr => $value:expr ),* $( , )?`: A comma-separated list of key-value pairs to insert into the `HashMap`. +/// Each key and value can be of any type that implements the `Into` and `Into` traits, where `K` and `V` are the +/// types stored in the `HashMap` as keys and values, respectively. +/// +/// # Returns +/// +/// Returns a `HashMap` containing all the specified key-value pairs. The capacity of the map is +/// automatically determined based on the number of elements provided. +/// +/// # Example +/// +/// Basic usage with string slices and integer values: +/// +/// ```rust +/// # use collection_tools::{ HashMap, hmap }; +/// let map : HashMap< &str, i32 > = hmap!( "one" => 1, "two" => 2, "three" => 3 ); +/// assert_eq!( map.get( "one" ), Some( &1 ) ); +/// assert_eq!( map.get( "two" ), Some( &2 ) ); +/// assert_eq!( map.get( "three" ), Some( &3 ) ); +/// ``` +/// +/// # Example +/// +/// Creating a `HashMap` of integers to strings from literals: +/// +/// ```rust +/// # use collection_tools::{ HashMap, hmap }; +/// let pairs = hmap!( 1 => "apple", 2 => "banana" ); +/// assert_eq!( pairs.get( &1 ), Some( &"apple" ) ); +/// assert_eq!( pairs.get( &2 ), Some( &"banana" ) ); +/// ``` +/// +#[ cfg( feature = "collection_constructors" ) ] +#[ macro_export( local_inner_macros ) ] +macro_rules! hmap +{ + ( + $( $key : expr => $value : expr ),* $( , )? + ) + => + {{ + let _cap = count!( @count $( $key ),* ); + let mut _map = $crate::hmap::HashMap::with_capacity( _cap ); + $( + let _ = _map.insert( $key, $value ); + )* + _map + }}; +} + +/// Creates a `HashMap` from a list of key-value pairs. +/// +/// The `into_hmap` macro allows for convenient creation of a `HashMap` with initial elements. +/// Keys and values passed to the macro are automatically converted into the map's key and value types +/// using `.into()`, enabling the use of literals or values of different, but convertible types. +/// +/// Note: The `into_hmap` macro relies on the `.into()` method to convert each key and value into the target types +/// of the `HashMap`. This means that the keys and values must be compatible with the `Into` and `Into` traits +/// for the key type `K` and value type `V` used in the `HashMap`. Also, this means that sometimes you must specify the type of collection's items. +/// +/// # 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 +/// +/// # Syntax +/// +/// The macro can be called with a comma-separated list of key-value pairs. A trailing comma is optional. +/// +/// ```rust +/// # use collection_tools::{ HashMap, into_hmap }; +/// // HashMap of &str to i32 +/// let map1 : HashMap< &str, i32 > = into_hmap!( "one" => 1, "two" => 2, "three" => 3 ); +/// +/// // HashMap of String to String +/// let map2 : HashMap< String, String > = into_hmap!{ "name".to_string() => "value".to_string(), "type" => "example" }; +/// +/// // With trailing comma +/// let map3 : HashMap< i32, &str > = into_hmap!( 1 => "one", 2 => "two", 3 => "three", ); +/// ``` +/// +/// # Parameters +/// +/// - `$( $key:expr => $value:expr ),* $( , )?`: A comma-separated list of key-value pairs to insert into the `HashMap`. +/// Each key and value can be of any type that implements the `Into` and `Into` traits, where `K` and `V` are the +/// types stored in the `HashMap` as keys and values, respectively. +/// +/// # Returns +/// +/// Returns a `HashMap` containing all the specified key-value pairs. The capacity of the map is +/// automatically determined based on the number of elements provided. +/// +/// # Example +/// +/// Basic usage with string slices and integer values: +/// +/// ```rust +/// # use collection_tools::{ HashMap, into_hmap }; +/// let map : HashMap< &str, i32 > = into_hmap!( "one" => 1, "two" => 2, "three" => 3 ); +/// assert_eq!( map.get( "one" ), Some( &1 ) ); +/// assert_eq!( map.get( "two" ), Some( &2 ) ); +/// assert_eq!( map.get( "three" ), Some( &3 ) ); +/// ``` +/// +/// # Example +/// +/// Using with different types that implement `Into` and `Into`: +/// +/// ```rust +/// # use collection_tools::{ HashMap, into_hmap }; +/// let items : HashMap< String, i32 > = into_hmap!( "pen" => 10, "book" => 45, "eraser" => 5 ); +/// assert_eq!( items.get( &"pen".to_string() ), Some(&10 ) ); +/// assert_eq!( items.get( &"book".to_string() ), Some(&45 ) ); +/// ``` +/// +/// # Example +/// +/// Creating a `HashMap` of integers to strings from literals: +/// +/// ```rust +/// # use collection_tools::{ HashMap, into_hmap }; +/// let pairs : HashMap< i32, String > = into_hmap!( 1 => "apple", 2 => "banana" ); +/// assert_eq!( pairs.get( &1 ), Some( &"apple".to_string() ) ); +/// assert_eq!( pairs.get( &2 ), Some( &"banana".to_string() ) ); +/// ``` +/// +#[ cfg( feature = "collection_into_constructors" ) ] +#[ macro_export( local_inner_macros ) ] +macro_rules! into_hmap +{ + ( + $( $key : expr => $value : expr ),* $( , )? + ) + => + {{ + let _cap = count!( @count $( $key ),* ); + let mut _map = $crate::hmap::HashMap::with_capacity( _cap ); + $( + let _ = _map.insert( Into::into( $key ), Into::into( $value ) ); + )* + _map + }}; +} diff --git a/module/core/collection_tools/src/collections/hset.rs b/module/core/collection_tools/src/collections/hset.rs new file mode 100644 index 0000000000..b9b2d682da --- /dev/null +++ b/module/core/collection_tools/src/collections/hset.rs @@ -0,0 +1,182 @@ +#[ cfg( feature = "use_alloc" ) ] +#[ doc( inline ) ] +#[ allow( unused_imports ) ] +pub use crate::dependency::hashbrown::hash_set::*; +#[ cfg( not( feature = "no_std" ) ) ] +#[ doc( inline ) ] +#[ allow( unused_imports ) ] +pub use std::collections::hash_set::*; + +/// 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 +/// +/// # Syntax +/// +/// The macro can be called with a comma-separated list of elements. A trailing comma is optional. +/// +/// ```rust +/// # use collection_tools::{ HashSet, hset }; +/// // HashSet of &str +/// let set1 = hset!( "a", "b", "c" ); +/// +/// // HashSet of &str +/// let set2 = hset!{ "a", "b", "c" }; +/// +/// // With trailing comma +/// let set3 = hset!( 1, 2, 3, ); +/// ``` +/// +/// # Parameters +/// +/// - `$( $key:expr ),* $( , )?`: A comma-separated list of elements to insert into the `HashSet`. +/// Each element can be of any type that implements the `Into< T >` trait, where `T` is the +/// type stored in the `HashSet`. +/// +/// # Returns +/// +/// Returns a `HashSet` containing all the specified elements. The capacity of the set is +/// automatically determined based on the number of elements provided. +/// +/// # Example +/// +/// Basic usage with string slices: +/// +/// ```rust +/// # use collection_tools::{ HashSet, hset }; +/// let set = hset!( "one", "two", "three" ); +/// assert!( set.contains( "one" ) ); +/// assert!( set.contains( "two" ) ); +/// assert!( set.contains( "three" ) ); +/// assert_eq!( set.len(), 3 ); +/// ``` +/// +/// # Example +/// +/// Creating a `HashSet` of `&str` from string literals: +/// +/// ```rust +/// # use collection_tools::{ HashSet, hset }; +/// let s = hset!{ "value" }; +/// assert_eq!( s.get( "value" ), Some( &"value" ) ); +/// ``` +/// +#[ cfg( feature = "collection_constructors" ) ] +#[ macro_export( local_inner_macros ) ] +macro_rules! hset +{ + ( + $( $key : expr ),* $( , )? + ) + => + {{ + let _cap = count!( @count $( $key ),* ); + let mut _set = $crate::hset::HashSet::with_capacity( _cap ); + $( + let _ = _set.insert( $key ); + )* + _set + }}; +} + +/// Creates a `HashSet` from a list of elements. +/// +/// The `into_hset` macro allows for convenient creation of a `HashSet` with initial elements. +/// Elements passed to the macro are automatically converted into the set's element type +/// using `.into()`, facilitating the use of literals or values of different, but convertible types. +/// +/// Note: The `into_hset` macro relies on the `.into()` method to convert each element into the target type +/// of the `HashSet`. This means that the elements must be compatible with the `Into< T >` trait for the +/// type `T` used in the `HashSet`. Also, this means that sometimes you must specify the type of collection's items. +/// +/// # 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 +/// +/// # Syntax +/// +/// The macro can be called with a comma-separated list of elements. A trailing comma is optional. +/// +/// ```rust +/// # use collection_tools::{ HashSet, into_hset }; +/// // HashSet of &str +/// let set1 : HashSet< &str > = into_hset!( "a", "b", "c" ); +/// +/// // HashSet of String +/// let set2 : HashSet< String > = into_hset!{ "a".to_string(), "b", "c" }; +/// +/// // With trailing comma +/// let set3 : HashSet< i32 > = into_hset!( 1, 2, 3, ); +/// ``` +/// +/// # Parameters +/// +/// - `$( $key:expr ),* $( , )?`: A comma-separated list of elements to insert into the `HashSet`. +/// Each element can be of any type that implements the `Into< T >` trait, where `T` is the +/// type stored in the `HashSet`. +/// +/// # Returns +/// +/// Returns a `HashSet` containing all the specified elements. The capacity of the set is +/// automatically determined based on the number of elements provided. +/// +/// # Example +/// +/// Basic usage with string slices: +/// +/// ```rust +/// # use collection_tools::{ HashSet, into_hset }; +/// let set : HashSet< &str > = into_hset!( "one", "two", "three" ); +/// assert!( set.contains( "one" ) ); +/// assert!( set.contains( "two" ) ); +/// assert!( set.contains( "three" ) ); +/// assert_eq!( set.len(), 3 ); +/// ``` +/// +/// # Example +/// +/// Using with different types that implement `Into< T >`: +/// +/// ```rust +/// # use collection_tools::{ HashSet, into_hset }; +/// let numbers : HashSet< i32 > = into_hset!( 1, 2, 3 ); +/// assert!( numbers.contains( &1 ) ); +/// assert!( numbers.contains( &2 ) ); +/// assert!( numbers.contains( &3 ) ); +/// ``` +/// +/// # Example +/// +/// Creating a `HashSet` of `String` from string literals: +/// +/// ```rust +/// # use collection_tools::{ HashSet, into_hset }; +/// let s : HashSet< String > = into_hset!{ "value" }; +/// assert_eq!( s.get( "value" ), Some( &"value".to_string() ) ); +/// ``` +/// +#[ cfg( feature = "collection_into_constructors" ) ] +#[ macro_export( local_inner_macros ) ] +macro_rules! into_hset +{ + ( + $( $key : expr ),* $( , )? + ) + => + {{ + let _cap = count!( @count $( $key ),* ); + let mut _set = $crate::hset::HashSet::with_capacity( _cap ); + $( + let _ = _set.insert( Into::into( $key ) ); + )* + _set + }}; +} diff --git a/module/core/collection_tools/src/collections/list.rs b/module/core/collection_tools/src/collections/list.rs new file mode 100644 index 0000000000..d7088ea77f --- /dev/null +++ b/module/core/collection_tools/src/collections/list.rs @@ -0,0 +1,173 @@ +#[ doc( inline ) ] +#[ allow( unused_imports ) ] +pub use alloc::collections::linked_list::*; + +/// Creates a `LinkedList` from a list of elements. +/// +/// The `list` macro facilitates the creation of a `LinkedList` with initial elements. +/// +/// # Origin +/// +/// This collection is reexported from `alloc`. +/// +/// # Syntax +/// +/// The macro can be called with a comma-separated list of elements. A trailing comma is optional. +/// +/// ```rust +/// # use collection_tools::{ LinkedList, list }; +/// // LinkedList of i32 +/// let lst1 = list!( 1, 2, 3, 4, 5 ); +/// +/// // LinkedList of &str +/// let lst2 = list!{ "hello", "world", "rust" }; +/// +/// // With trailing comma +/// let lst3 = list!( 1.1, 2.2, 3.3, ); +/// ``` +/// +/// # Parameters +/// +/// - `$( $key:expr ),* $( , )?`: A comma-separated list of elements to insert into the `LinkedList`. +/// Each element can be of any type that implements the `Into` trait, where `T` is the +/// type stored in the `LinkedList`. +/// +/// # Returns +/// +/// Returns a `LinkedList` containing all the specified elements. The capacity of the list is +/// dynamically adjusted based on the number of elements provided. +/// +/// # Example +/// +/// Basic usage with integers: +/// +/// ```rust +/// # use collection_tools::{ LinkedList, list }; +/// let lst = list!( 1, 2, 3 ); +/// assert_eq!( lst.front(), Some( &1 ) ); // The first element is 1 +/// assert_eq!( lst.back(), Some( &3 ) ); // The last element is 3 +/// ``` +/// +/// # Example +/// +/// Creating a `LinkedList` of `&str` from string literals: +/// +/// ```rust +/// # use collection_tools::{ LinkedList, list }; +/// let fruits = list!{ "apple", "banana", "cherry" }; +/// assert_eq!( fruits.front(), Some( &"apple" ) ); // The first element +/// assert_eq!( fruits.back(), Some( &"cherry" ) ); // The last element +/// ``` +/// +#[ cfg( feature = "collection_constructors" ) ] +#[ macro_export( local_inner_macros ) ] +macro_rules! list +{ + ( + $( $key : expr ),* $( , )? + ) + => + {{ + // "The LinkedList allows pushing and popping elements at either end in constant time." + // So no `with_capacity` + let mut _lst = $crate::list::LinkedList::new(); + $( + _lst.push_back( $key ); + )* + _lst + }}; +} + +/// Creates a `LinkedList` from a list of elements. +/// +/// The `into_list` macro facilitates the creation of a `LinkedList` with initial elements. +/// Elements passed to the macro are automatically converted into the list's element type +/// using `.into()`, making it convenient to use literals or values of different, but convertible types. +/// +/// Note: The `into_list` macro leverages the `.into()` method to convert each element into the target type +/// of the `LinkedList`. Therefore, the elements must be compatible with the `Into` trait for the +/// type `T` used in the `LinkedList`. Also, this means that sometimes you must specify the type of collection's items. +/// +/// # Origin +/// +/// This collection is reexported from `alloc`. +/// +/// # Syntax +/// +/// The macro can be called with a comma-separated list of elements. A trailing comma is optional. +/// +/// ```rust +/// # use collection_tools::{ LinkedList, into_list }; +/// // LinkedList of i32 +/// let lst1 : LinkedList< i32 > = into_list!( 1, 2, 3, 4, 5 ); +/// +/// // LinkedList of String +/// let lst2 : LinkedList< String > = into_list!{ "hello".to_string(), "world", "rust" }; +/// +/// // With trailing comma +/// let lst3 : LinkedList< f64 > = into_list!( 1.1, 2.2, 3.3, ); +/// ``` +/// +/// # Parameters +/// +/// - `$( $key:expr ),* $( , )?`: A comma-separated list of elements to insert into the `LinkedList`. +/// Each element can be of any type that implements the `Into` trait, where `T` is the +/// type stored in the `LinkedList`. +/// +/// # Returns +/// +/// Returns a `LinkedList` containing all the specified elements. The capacity of the list is +/// dynamically adjusted based on the number of elements provided. +/// +/// # Example +/// +/// Basic usage with integers: +/// +/// ```rust +/// # use collection_tools::{ LinkedList, into_list }; +/// let lst: LinkedList< i32 > = into_list!( 1, 2, 3 ); +/// assert_eq!( lst.front(), Some( &1 ) ); // The first element is 1 +/// assert_eq!( lst.back(), Some( &3 ) ); // The last element is 3 +/// ``` +/// +/// # Example +/// +/// Using with different types that implement `Into`: +/// +/// ```rust +/// # use collection_tools::{ LinkedList, into_list }; +/// let chars : LinkedList< String > = into_list!( "a", "b", "c" ); +/// assert!( chars.contains( &"a".to_string() ) ); +/// assert!( chars.contains( &"b".to_string() ) ); +/// assert!( chars.contains( &"c".to_string() ) ); +/// ``` +/// +/// # Example +/// +/// Creating a `LinkedList` of `String` from string literals: +/// +/// ```rust +/// # use collection_tools::{ LinkedList, into_list }; +/// let fruits : LinkedList< String > = into_list!{ "apple", "banana", "cherry" }; +/// assert_eq!( fruits.front(), Some( &"apple".to_string() ) ); // The first element +/// assert_eq!( fruits.back(), Some( &"cherry".to_string() ) ); // The last element +/// ``` +/// +#[ cfg( feature = "collection_into_constructors" ) ] +#[ macro_export( local_inner_macros ) ] +macro_rules! into_list +{ + ( + $( $key : expr ),* $( , )? + ) + => + {{ + // "The LinkedList allows pushing and popping elements at either end in constant time." + // So no `with_capacity` + let mut _lst = $crate::list::LinkedList::new(); + $( + _lst.push_back( Into::into( $key ) ); + )* + _lst + }}; +} diff --git a/module/core/collection_tools/src/collections/vec.rs b/module/core/collection_tools/src/collections/vec.rs new file mode 100644 index 0000000000..2c19db388f --- /dev/null +++ b/module/core/collection_tools/src/collections/vec.rs @@ -0,0 +1,176 @@ +#[ doc( inline ) ] +#[ allow( unused_imports ) ] +pub use alloc::vec::*; +#[ doc( inline ) ] +#[ allow( unused_imports ) ] +pub use core::slice::{ Iter, IterMut }; + +/// Creates a `Vec` from a list of elements. +/// +/// The `vec` macro simplifies the creation of a `Vec` with initial elements. +/// +/// # Origin +/// +/// This collection is reexported from `alloc`. +/// +/// # Syntax +/// +/// The macro can be called with a comma-separated list of elements. A trailing comma is optional. +/// +/// ```rust +/// # use collection_tools::{Vec, vec}; +/// // Vec of i32 +/// let vec1 = vec!( 1, 2, 3, 4, 5 ); +/// +/// // Vec of &str +/// let vec2 = vec!{ "hello", "world", "rust" }; +/// +/// // With trailing comma +/// let vec3 = vec!( 1.1, 2.2, 3.3, ); +/// ``` +/// +/// # Parameters +/// +/// - `$( $key : expr ),* $( , )?`: A comma-separated list of elements to insert into the `Vec`. +/// Each element can be of any type that implements the `Into` trait, where `T` is the +/// type stored in the `Vec`. +/// +/// # Returns +/// +/// Returns a `Vec` containing all the specified elements. The capacity of the vector is +/// automatically determined based on the number of elements provided. +/// +/// # Example +/// +/// Basic usage with integers: +/// +/// ```rust +/// # use collection_tools::{Vec, vec}; +/// let vec = vec!( 1, 2, 3 ); +/// assert_eq!( vec[ 0 ], 1 ); +/// assert_eq!( vec[ 1 ], 2 ); +/// assert_eq!( vec[ 2 ], 3 ); +/// ``` +/// +/// # Example +/// +/// Creating a `Vec` of `&str` from string literals: +/// +/// ```rust +/// # use collection_tools::{Vec, vec}; +/// let mixed = vec!{ "value", "another value" }; +/// assert_eq!( mixed[ 0 ], "value" ); +/// assert_eq!( mixed[ 1 ], "another value" ); +/// ``` +/// +#[ cfg( feature = "collection_constructors" ) ] +#[ macro_export( local_inner_macros ) ] +macro_rules! vec +{ + ( + $( $key : expr ),* $( , )? + ) + => + {{ + let _cap = count!( @count $( $key ),* ); + let mut _vec = $crate::vec::Vec::with_capacity( _cap ); + $( + _vec.push( $key ); + )* + _vec + }}; +} + +/// Creates a `Vec` from a list of elements. +/// +/// The `into_vec!` macro simplifies the creation of a `Vec` with initial elements. +/// Elements passed to the macro are automatically converted into the vector's element type +/// using `.into()`, making it convenient to use literals or values of different, but convertible types. +/// +/// Note: The `into_vec!` macro utilizes the `.into()` method to convert each element into the target type +/// of the `Vec`. Therefore, the elements must be compatible with the `Into` trait for the +/// type `T` used in the `Vec`. Also, this means that sometimes you must specify the type of collection's items. +/// +/// # Origin +/// +/// This collection is reexported from `alloc`. +/// +/// # Syntax +/// +/// The macro can be called with a comma-separated list of elements. A trailing comma is optional. +/// +/// ```rust +/// # use collection_tools::{Vec, into_vec}; +/// // Vec of i32 +/// let vec1 : Vec< i32 > = into_vec!( 1, 2, 3, 4, 5 ); +/// +/// // Vec of String +/// let vec2 : Vec< String > = into_vec!{ "hello", "world", "rust" }; +/// +/// // With trailing comma +/// let vec3 : Vec< f64 > = into_vec!( 1.1, 2.2, 3.3, ); +/// ``` +/// +/// # Parameters +/// +/// - `$( $key : expr ),* $( , )?`: A comma-separated list of elements to insert into the `Vec`. +/// Each element can be of any type that implements the `Into` trait, where `T` is the +/// type stored in the `Vec`. +/// +/// # Returns +/// +/// Returns a `Vec` containing all the specified elements. The capacity of the vector is +/// automatically determined based on the number of elements provided. +/// +/// # Example +/// +/// Basic usage with integers: +/// +/// ```rust +/// # use collection_tools::{Vec, into_vec}; +/// let vec : Vec< i32 > = into_vec!( 1, 2, 3 ); +/// assert_eq!( vec[ 0 ], 1 ); +/// assert_eq!( vec[ 1 ], 2 ); +/// assert_eq!( vec[ 2 ], 3 ); +/// ``` +/// +/// # Example +/// +/// Using with different types that implement `Into`: +/// +/// ```rust +/// # use collection_tools::{Vec, into_vec}; +/// let words : Vec< String > = into_vec!( "alpha", "beta", "gamma" ); +/// assert_eq!( words[ 0 ], "alpha" ); +/// assert_eq!( words[ 1 ], "beta" ); +/// assert_eq!( words[ 2 ], "gamma" ); +/// ``` +/// +/// # Example +/// +/// Creating a `Vec` of `String` from string literals and String objects: +/// +/// ```rust +/// # use collection_tools::{Vec, into_vec}; +/// let mixed : Vec< String > = into_vec!{ "value", "another value".to_string() }; +/// assert_eq!( mixed[ 0 ], "value" ); +/// assert_eq!( mixed[ 1 ], "another value" ); +/// ``` +/// +#[ cfg( feature = "collection_into_constructors" ) ] +#[ macro_export( local_inner_macros ) ] +macro_rules! into_vec +{ + ( + $( $key : expr ),* $( , )? + ) + => + {{ + let _cap = count!( @count $( $key ),* ); + let mut _vec = $crate::vec::Vec::with_capacity( _cap ); + $( + _vec.push( Into::into( $key ) ); + )* + _vec + }}; +} diff --git a/module/core/collection_tools/src/collections/vecd.rs b/module/core/collection_tools/src/collections/vecd.rs new file mode 100644 index 0000000000..2edff8b433 --- /dev/null +++ b/module/core/collection_tools/src/collections/vecd.rs @@ -0,0 +1,177 @@ +#[ doc( inline ) ] +#[ allow( unused_imports ) ] +pub use alloc::collections::vec_deque::*; + +/// Creates a `VecDeque` from a list of elements. +/// +/// The `vecd` macro allows for the convenient creation of a `VecDeque` with initial elements. +/// Elements passed to the macro are automatically converted into the deque's element type +/// using `.into()`, enabling the use of literals or values of different, but convertible types. +/// +/// Note: The `vecd` macro relies on the `.into()` method to convert each element into the target type +/// of the `VecDeque`. This means that the elements must be compatible with the `Into` trait for the +/// type `T` used in the `VecDeque`. +/// +/// # Origin +/// +/// This collection is reexported from `alloc`. +/// +/// # Syntax +/// +/// The macro can be called with a comma-separated list of elements. A trailing comma is optional. +/// +/// ```rust +/// # use collection_tools::{ VecDeque, vecd }; +/// // VecDeque of i32 +/// let vd1 = vecd!( 1, 2, 3, 4, 5 ); +/// +/// // VecDeque of String +/// let vd2 = vecd!{ "hello", "world", "rust" }; +/// +/// // With trailing comma +/// let vd3 = vecd!( 1.1, 2.2, 3.3, ); +/// ``` +/// +/// # Parameters +/// +/// - `$( $key:expr ),* $( , )?`: A comma-separated list of elements to insert into the `VecDeque`. +/// Each element can be of any type that implements the `Into< T >` trait, where `T` is the +/// type stored in the `VecDeque`. +/// +/// # Returns +/// +/// Returns a `VecDeque` containing all the specified elements. The capacity of the deque is +/// automatically determined based on the number of elements provided. +/// +/// # Example +/// +/// Basic usage with integers: +/// +/// ```rust +/// # use collection_tools::{ VecDeque, vecd }; +/// let vd : VecDeque< i32 > = vecd!( 1, 2, 3 ); +/// assert_eq!( vd.front(), Some( &1 ) ); // The first element is 1 +/// assert_eq!( vd.back(), Some( &3 ) ); // The last element is 3 +/// ``` +/// +/// # Example +/// +/// Creating a `VecDeque` of `&str` from string literals: +/// +/// ```rust +/// # use collection_tools::{ VecDeque, vecd }; +/// let fruits = vecd!{ "apple", "banana", "cherry" }; +/// assert_eq!( fruits.front(), Some( &"apple" ) ); // The first element +/// assert_eq!( fruits.back(), Some( &"cherry" ) ); // The last element +/// ``` +/// +#[ cfg( feature = "collection_constructors" ) ] +#[ macro_export( local_inner_macros ) ] +macro_rules! vecd +{ + ( + $( $key : expr ),* $( , )? + ) + => + {{ + let _cap = count!( @count $( $key ),* ); + let mut _vecd = $crate::vecd::VecDeque::with_capacity( _cap ); + $( + _vecd.push_back( $key ); + )* + _vecd + }}; +} + +/// Creates a `VecDeque` from a list of elements. +/// +/// The `into_vecd` macro allows for the convenient creation of a `VecDeque` with initial elements. +/// Elements passed to the macro are automatically converted into the deque's element type +/// using `.into()`, enabling the use of literals or values of different, but convertible types. +/// +/// Note: The `into_vecd` macro relies on the `.into()` method to convert each element into the target type +/// of the `VecDeque`. This means that the elements must be compatible with the `Into` trait for the +/// type `T` used in the `VecDeque`. +/// +/// # Origin +/// +/// This collection is reexported from `alloc`. +/// +/// # Syntax +/// +/// The macro can be called with a comma-separated list of elements. A trailing comma is optional. +/// +/// ```rust +/// # use collection_tools::{ VecDeque, into_vecd }; +/// // VecDeque of i32 +/// let vd1 : VecDeque< i32 > = into_vecd!( 1, 2, 3, 4, 5 ); +/// +/// // VecDeque of String +/// let vd2 : VecDeque< String > = into_vecd!{ "hello".to_string(), "world", "rust" }; +/// +/// // With trailing comma +/// let vd3 : VecDeque< f64 > = into_vecd!( 1.1, 2.2, 3.3, ); +/// ``` +/// +/// # Parameters +/// +/// - `$( $key:expr ),* $( , )?`: A comma-separated list of elements to insert into the `VecDeque`. +/// Each element can be of any type that implements the `Into< T >` trait, where `T` is the +/// type stored in the `VecDeque`. +/// +/// # Returns +/// +/// Returns a `VecDeque` containing all the specified elements. The capacity of the deque is +/// automatically determined based on the number of elements provided. +/// +/// # Example +/// +/// Basic usage with integers: +/// +/// ```rust +/// # use collection_tools::{ VecDeque, into_vecd }; +/// let vd : VecDeque< i32 > = into_vecd!( 1, 2, 3 ); +/// assert_eq!( vd.front(), Some( &1 ) ); // The first element is 1 +/// assert_eq!( vd.back(), Some( &3 ) ); // The last element is 3 +/// ``` +/// +/// # Example +/// +/// Using with different types that implement `Into< T >`: +/// +/// ```rust +/// # use collection_tools::{ VecDeque, into_vecd }; +/// let chars : VecDeque< char > = into_vecd!( 'a', 'b', 'c' ); +/// assert!( chars.contains( &'a' ) ); +/// assert!( chars.contains( &'b' ) ); +/// assert!( chars.contains( &'c' ) ); +/// ``` +/// +/// # Example +/// +/// Creating a `VecDeque` of `String` from string literals: +/// +/// ```rust +/// # use collection_tools::{ VecDeque, into_vecd }; +/// let fruits : VecDeque< String > = into_vecd!{ "apple", "banana", "cherry" }; +/// assert_eq!( fruits.front(), Some( &"apple".to_string() ) ); // The first element +/// assert_eq!( fruits.back(), Some( &"cherry".to_string() ) ); // The last element +/// ``` +/// +#[ cfg( feature = "collection_into_constructors" ) ] +#[ macro_export( local_inner_macros ) ] +macro_rules! into_vecd +{ + ( + $( $key : expr ),* $( , )? + ) + => + {{ + let _cap = count!( @count $( $key ),* ); + let mut _vecd = $crate::vecd::VecDeque::with_capacity( _cap ); + $( + _vecd.push_back( Into::into( $key ) ); + )* + _vecd + }}; +} diff --git a/module/core/collection_tools/src/constructors.rs b/module/core/collection_tools/src/constructors.rs deleted file mode 100644 index 1ea42a5e62..0000000000 --- a/module/core/collection_tools/src/constructors.rs +++ /dev/null @@ -1,582 +0,0 @@ -/// Creates a `BTreeMap` from a list of key-value pairs. -/// -/// The `bmap` macro facilitates the convenient creation of a `BTreeMap` with initial elements. -/// -/// # Origin -/// -/// This collection is reexported from `alloc`. -/// -/// # Syntax -/// -/// The macro can be called with a comma-separated list of key-value pairs. A trailing comma is optional. -/// -/// ```rust -/// # use collection_tools::{ BTreeMap, bmap }; -/// // BTreeMap of &str to i32 -/// let map1 = bmap!( "one" => 1, "two" => 2, "three" => 3 ); -/// -/// // BTreeMap of &str to &str -/// let map2 = bmap!{ "name" => "value" }; -/// -/// // With trailing comma -/// let map3 = bmap!( 1 => "one", 2 => "two", 3 => "three", ); -/// ``` -/// -/// # Parameters -/// -/// - `$( $key:expr => $value:expr ),* $( , )?`: A comma-separated list of key-value pairs to insert into the `BTreeMap`. -/// Each key and value can be of any type that implements the `Into< K >` and `Into< V >` traits, where `K` and `V` are the -/// types stored in the `BTreeMap` as keys and values, respectively. -/// -/// # Returns -/// -/// Returns a `BTreeMap` containing all the specified key-value pairs. The map's capacity is -/// automatically determined based on the number of elements provided. -/// -/// # Example -/// -/// Basic usage with string slices and integer values: -/// -/// ```rust -/// # use collection_tools::{ BTreeMap, bmap }; -/// let map = bmap!( "one" => 1, "two" => 2, "three" => 3 ); -/// assert_eq!( map.get( "one" ), Some( &1 ) ); -/// assert_eq!( map.get( "two" ), Some( &2 ) ); -/// assert_eq!( map.get( "three" ), Some( &3 ) ); -/// ``` -/// -/// # Example -/// -/// Creating a `BTreeMap` of integers to string slices from literals: -/// -/// ```rust -/// # use collection_tools::{ BTreeMap, bmap }; -/// let numbers = bmap!( 1 => "one", 2 => "two", 3 => "three" ); -/// assert_eq!( numbers.get( &1 ), Some( &"one" ) ); -/// assert_eq!( numbers.get( &2 ), Some( &"two" ) ); -/// assert_eq!( numbers.get( &3 ), Some( &"three" ) ); -/// ``` -/// -#[ macro_export( local_inner_macros ) ] -macro_rules! bmap -{ - ( - $( $key : expr => $value : expr ),* $( , )? - ) - => - {{ - let mut _map = collection_tools::BTreeMap::new(); - $( - let _ = _map.insert( $key , $value ); - )* - _map - }}; -} - -/// Creates a `BTreeSet` from a list of elements. -/// -/// The `bset` macro allows for convenient creation of a `BTreeSet` with initial elements. -/// -/// # Origin -/// -/// This collection is reexported from `alloc`. -/// -/// # Syntax -/// -/// The macro can be called with a comma-separated list of elements. A trailing comma is optional. -/// -/// ```rust -/// # use collection_tools::{ BTreeSet, bset }; -/// // BTreeSet of &str -/// let set1 = bset!( "a", "b", "c" ); -/// -/// // With trailing comma -/// let set3 = bset!( 1, 2, 3, ); -/// ``` -/// -/// # Parameters -/// -/// - `$( $key:expr ),* $( , )?`: A comma-separated list of elements to insert into the `BTreeSet`. -/// Each element can be of any type that implements the `Into` trait, where `T` is the -/// type stored in the `BTreeSet`. -/// -/// # Returns -/// -/// Returns a `BTreeSet` containing all the specified elements. The capacity of the set is -/// automatically determined based on the number of elements provided. -/// -/// # Example -/// -/// Basic usage with string slices: -/// -/// ```rust -/// # use collection_tools::{ BTreeSet, bset }; -/// let set = bset!( "one", "two", "three" ); -/// assert!( set.contains( "one" ) ); -/// assert!( set.contains( "two" ) ); -/// assert!( set.contains( "three" ) ); -/// assert_eq!( set.len(), 3 ); -/// ``` -/// -#[ macro_export( local_inner_macros ) ] -macro_rules! bset -{ - ( - $( $key : expr ),* $( , )? - ) - => - {{ - let mut _set = collection_tools::BTreeSet::new(); - $( - _set.insert( $key ); - )* - _set - }}; -} - -/// Creates a `BinaryHeap` from a list of elements. -/// -/// The `into_heap` macro simplifies the creation of a `BinaryHeap` with initial elements. -/// -/// # Origin -/// -/// This collection is reexported from `alloc`. -/// -/// # Syntax -/// -/// The macro can be called with a comma-separated list of elements. A trailing comma is optional. -/// -/// ```rust -/// # use collection_tools::{ BinaryHeap, heap }; -/// // BinaryHeap of i32 -/// let heap1 = heap!( 3, 1, 4, 1, 5, 9 ); -/// -/// // BinaryHeap of &str -/// let heap2 = heap!{ "pear", "apple", "banana" }; -/// -/// // With trailing comma -/// let heap3 = heap!( 2, 7, 1, 8, ); -/// ``` -/// -/// # Parameters -/// -/// - `$( $key:expr ),* $( , )?`: A comma-separated list of elements to insert into the `BinaryHeap`. -/// Each element can be of any type that implements the `Into` trait, where `T` is the -/// type stored in the `BinaryHeap`. -/// -/// # Returns -/// -/// Returns a `BinaryHeap` containing all the specified elements. The capacity of the heap is -/// automatically determined based on the number of elements provided. -/// -/// # Example -/// -/// Basic usage with integers: -/// -/// ```rust -/// # use collection_tools::{ BinaryHeap, heap }; -/// let heap = heap!( 5, 3, 7, 1 ); -/// assert_eq!( heap.peek(), Some( &7 ) ); // The largest value is at the top of the heap -/// ``` -/// -#[ macro_export( local_inner_macros ) ] -macro_rules! heap -{ - ( - $( $key : expr ),* $( , )? - ) - => - {{ - let _cap = count!( @count $( $key ),* ); - let mut _heap = collection_tools::BinaryHeap::with_capacity( _cap ); - $( - _heap.push( $key ); - )* - _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: -/// - from `std`, if `no_std` flag if off -/// - from `hashbrown`, if `use_alloc` flag if on -/// -/// # Syntax -/// -/// The macro can be called with a comma-separated list of key-value pairs. A trailing comma is optional. -/// -/// ```rust -/// # use collection_tools::{ HashMap, hmap }; -/// // HashMap of &str to i32 -/// let map1 = hmap!( "one" => 1, "two" => 2, "three" => 3 ); -/// -/// // HashMap of &str to &str -/// let map2 = hmap!{ "name" => "value", "type" => "example" }; -/// -/// // With trailing comma -/// let map3 = hmap!( 1 => "one", 2 => "two", 3 => "three", ); -/// ``` -/// -/// # Parameters -/// -/// - `$( $key:expr => $value:expr ),* $( , )?`: A comma-separated list of key-value pairs to insert into the `HashMap`. -/// Each key and value can be of any type that implements the `Into` and `Into` traits, where `K` and `V` are the -/// types stored in the `HashMap` as keys and values, respectively. -/// -/// # Returns -/// -/// Returns a `HashMap` containing all the specified key-value pairs. The capacity of the map is -/// automatically determined based on the number of elements provided. -/// -/// # Example -/// -/// Basic usage with string slices and integer values: -/// -/// ```rust -/// # use collection_tools::{ HashMap, hmap }; -/// let map : HashMap< &str, i32 > = hmap!( "one" => 1, "two" => 2, "three" => 3 ); -/// assert_eq!( map.get( "one" ), Some( &1 ) ); -/// assert_eq!( map.get( "two" ), Some( &2 ) ); -/// assert_eq!( map.get( "three" ), Some( &3 ) ); -/// ``` -/// -/// # Example -/// -/// Creating a `HashMap` of integers to strings from literals: -/// -/// ```rust -/// # use collection_tools::{ HashMap, hmap }; -/// let pairs = hmap!( 1 => "apple", 2 => "banana" ); -/// assert_eq!( pairs.get( &1 ), Some( &"apple" ) ); -/// assert_eq!( pairs.get( &2 ), Some( &"banana" ) ); -/// ``` -/// -#[macro_export(local_inner_macros)] -macro_rules! hmap -{ - ( - $( $key : expr => $value : expr ),* $( , )? - ) - => - {{ - let _cap = count!( @count $( $key ),* ); - let mut _map = collection_tools::HashMap::with_capacity( _cap ); - $( - let _ = _map.insert( $key, $value ); - )* - _map - }}; -} - -/// 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 -/// -/// # Syntax -/// -/// The macro can be called with a comma-separated list of elements. A trailing comma is optional. -/// -/// ```rust -/// # use collection_tools::{ HashSet, hset }; -/// // HashSet of &str -/// let set1 = hset!( "a", "b", "c" ); -/// -/// // HashSet of &str -/// let set2 = hset!{ "a", "b", "c" }; -/// -/// // With trailing comma -/// let set3 = hset!( 1, 2, 3, ); -/// ``` -/// -/// # Parameters -/// -/// - `$( $key:expr ),* $( , )?`: A comma-separated list of elements to insert into the `HashSet`. -/// Each element can be of any type that implements the `Into< T >` trait, where `T` is the -/// type stored in the `HashSet`. -/// -/// # Returns -/// -/// Returns a `HashSet` containing all the specified elements. The capacity of the set is -/// automatically determined based on the number of elements provided. -/// -/// # Example -/// -/// Basic usage with string slices: -/// -/// ```rust -/// # use collection_tools::{ HashSet, hset }; -/// let set = hset!( "one", "two", "three" ); -/// assert!( set.contains( "one" ) ); -/// assert!( set.contains( "two" ) ); -/// assert!( set.contains( "three" ) ); -/// assert_eq!( set.len(), 3 ); -/// ``` -/// -/// # Example -/// -/// Creating a `HashSet` of `&str` from string literals: -/// -/// ```rust -/// # use collection_tools::{ HashSet, hset }; -/// let s = hset!{ "value" }; -/// assert_eq!( s.get( "value" ), Some( &"value" ) ); -/// ``` -/// -#[ macro_export( local_inner_macros ) ] -macro_rules! hset -{ - ( - $( $key : expr ),* $( , )? - ) - => - {{ - let _cap = count!( @count $( $key ),* ); - let mut _set = collection_tools::HashSet::with_capacity( _cap ); - $( - let _ = _set.insert( $key ); - )* - _set - }}; -} - -/// Creates a `LinkedList` from a list of elements. -/// -/// The `list` macro facilitates the creation of a `LinkedList` with initial elements. -/// -/// -/// # Origin -/// -/// This collection is reexported from `alloc`. -/// -/// # Syntax -/// -/// The macro can be called with a comma-separated list of elements. A trailing comma is optional. -/// -/// ```rust -/// # use collection_tools::{ LinkedList, list }; -/// // LinkedList of i32 -/// let lst1 = list!( 1, 2, 3, 4, 5 ); -/// -/// // LinkedList of &str -/// let lst2 = list!{ "hello", "world", "rust" }; -/// -/// // With trailing comma -/// let lst3 = list!( 1.1, 2.2, 3.3, ); -/// ``` -/// -/// # Parameters -/// -/// - `$( $key:expr ),* $( , )?`: A comma-separated list of elements to insert into the `LinkedList`. -/// Each element can be of any type that implements the `Into` trait, where `T` is the -/// type stored in the `LinkedList`. -/// -/// # Returns -/// -/// Returns a `LinkedList` containing all the specified elements. The capacity of the list is -/// dynamically adjusted based on the number of elements provided. -/// -/// # Example -/// -/// Basic usage with integers: -/// -/// ```rust -/// # use collection_tools::{ LinkedList, list }; -/// let lst = list!( 1, 2, 3 ); -/// assert_eq!( lst.front(), Some( &1 ) ); // The first element is 1 -/// assert_eq!( lst.back(), Some( &3 ) ); // The last element is 3 -/// ``` -/// -/// # Example -/// -/// Creating a `LinkedList` of `&str` from string literals: -/// -/// ```rust -/// # use collection_tools::{ LinkedList, list }; -/// let fruits = list!{ "apple", "banana", "cherry" }; -/// assert_eq!( fruits.front(), Some( &"apple" ) ); // The first element -/// assert_eq!( fruits.back(), Some( &"cherry" ) ); // The last element -/// ``` -/// -#[ macro_export( local_inner_macros ) ] -macro_rules! list -{ - ( - $( $key : expr ),* $( , )? - ) - => - {{ - // "The LinkedList allows pushing and popping elements at either end in constant time." - // So no `with_capacity` - let mut _lst = collection_tools::LinkedList::new(); - $( - _lst.push_back( $key ); - )* - _lst - }}; -} - -/// Creates a `Vec` from a list of elements. -/// -/// The `vec` macro simplifies the creation of a `Vec` with initial elements. -/// -/// # Origin -/// -/// This collection is reexported from `alloc`. -/// -/// # Syntax -/// -/// The macro can be called with a comma-separated list of elements. A trailing comma is optional. -/// -/// ```rust -/// # use collection_tools::{Vec, vec}; -/// // Vec of i32 -/// let vec1 = vec!( 1, 2, 3, 4, 5 ); -/// -/// // Vec of &str -/// let vec2 = vec!{ "hello", "world", "rust" }; -/// -/// // With trailing comma -/// let vec3 = vec!( 1.1, 2.2, 3.3, ); -/// ``` -/// -/// # Parameters -/// -/// - `$( $key : expr ),* $( , )?`: A comma-separated list of elements to insert into the `Vec`. -/// Each element can be of any type that implements the `Into` trait, where `T` is the -/// type stored in the `Vec`. -/// -/// # Returns -/// -/// Returns a `Vec` containing all the specified elements. The capacity of the vector is -/// automatically determined based on the number of elements provided. -/// -/// # Example -/// -/// Basic usage with integers: -/// -/// ```rust -/// # use collection_tools::{Vec, vec}; -/// let vec = vec!( 1, 2, 3 ); -/// assert_eq!( vec[ 0 ], 1 ); -/// assert_eq!( vec[ 1 ], 2 ); -/// assert_eq!( vec[ 2 ], 3 ); -/// ``` -/// -/// # Example -/// -/// Creating a `Vec` of `&str` from string literals: -/// -/// ```rust -/// # use collection_tools::{Vec, vec}; -/// let mixed = vec!{ "value", "another value" }; -/// assert_eq!( mixed[ 0 ], "value" ); -/// assert_eq!( mixed[ 1 ], "another value" ); -/// ``` -/// -#[ macro_export( local_inner_macros ) ] -macro_rules! vec -{ - ( - $( $key : expr ),* $( , )? - ) - => - {{ - let _cap = count!( @count $( $key ),* ); - let mut _vec = collection_tools::Vec::with_capacity( _cap ); - $( - _vec.push( $key ); - )* - _vec - }}; -} - -/// Creates a `VecDeque` from a list of elements. -/// -/// The `vecd` macro allows for the convenient creation of a `VecDeque` with initial elements. -/// Elements passed to the macro are automatically converted into the deque's element type -/// using `.into()`, enabling the use of literals or values of different, but convertible types. -/// -/// Note: The `vecd` macro relies on the `.into()` method to convert each element into the target type -/// of the `VecDeque`. This means that the elements must be compatible with the `Into` trait for the -/// type `T` used in the `VecDeque`. -/// -/// # Origin -/// -/// This collection is reexported from `alloc`. -/// -/// # Syntax -/// -/// The macro can be called with a comma-separated list of elements. A trailing comma is optional. -/// -/// ```rust -/// # use collection_tools::{ VecDeque, vecd }; -/// // VecDeque of i32 -/// let vd1 = vecd!( 1, 2, 3, 4, 5 ); -/// -/// // VecDeque of String -/// let vd2 = vecd!{ "hello", "world", "rust" }; -/// -/// // With trailing comma -/// let vd3 = vecd!( 1.1, 2.2, 3.3, ); -/// ``` -/// -/// # Parameters -/// -/// - `$( $key:expr ),* $( , )?`: A comma-separated list of elements to insert into the `VecDeque`. -/// Each element can be of any type that implements the `Into< T >` trait, where `T` is the -/// type stored in the `VecDeque`. -/// -/// # Returns -/// -/// Returns a `VecDeque` containing all the specified elements. The capacity of the deque is -/// automatically determined based on the number of elements provided. -/// -/// # Example -/// -/// Basic usage with integers: -/// -/// ```rust -/// # use collection_tools::{ VecDeque, vecd }; -/// let vd : VecDeque< i32 > = vecd!( 1, 2, 3 ); -/// assert_eq!( vd.front(), Some( &1 ) ); // The first element is 1 -/// assert_eq!( vd.back(), Some( &3 ) ); // The last element is 3 -/// ``` -/// -/// # Example -/// -/// Creating a `VecDeque` of `&str` from string literals: -/// -/// ```rust -/// # use collection_tools::{ VecDeque, vecd }; -/// let fruits = vecd!{ "apple", "banana", "cherry" }; -/// assert_eq!( fruits.front(), Some( &"apple" ) ); // The first element -/// assert_eq!( fruits.back(), Some( &"cherry" ) ); // The last element -/// ``` -/// -#[ macro_export( local_inner_macros ) ] -macro_rules! vecd -{ - ( - $( $key : expr ),* $( , )? - ) - => - {{ - let _cap = count!( @count $( $key ),* ); - let mut _vecd = collection_tools::VecDeque::with_capacity( _cap ); - $( - _vecd.push_back( $key ); - )* - _vecd - }}; -} diff --git a/module/core/collection_tools/src/into_constructors.rs b/module/core/collection_tools/src/into_constructors.rs deleted file mode 100644 index 59af857a21..0000000000 --- a/module/core/collection_tools/src/into_constructors.rs +++ /dev/null @@ -1,738 +0,0 @@ -/// Creates a `BTreeMap` from a list of key-value pairs. -/// -/// The `into_bmap` macro facilitates the convenient creation of a `BTreeMap` with initial elements. -/// Keys and values passed to the macro are automatically converted into the map's key and value types -/// using `.into()`, enabling the use of literals or values of different, but convertible types. -/// -/// Note: The `into_bmap` macro relies on the `.into()` method to convert each key and value into the target types -/// of the `BTreeMap`. This means that the keys and values must be compatible with the `Into< K >` and `Into< V >` traits -/// for the key type `K` and value type `V` used in the `BTreeMap`. Also, this means that sometimes you must specify the type of collection's items. -/// -/// # Origin -/// -/// This collection is reexported from `alloc`. -/// -/// # Syntax -/// -/// The macro can be called with a comma-separated list of key-value pairs. A trailing comma is optional. -/// -/// ```rust -/// # use collection_tools::{ BTreeMap, into_bmap }; -/// // BTreeMap of &str to i32 -/// let map1 : BTreeMap< &str, i32 > = into_bmap!( "one" => 1, "two" => 2, "three" => 3 ); -/// -/// // BTreeMap of String to String -/// let map2 : BTreeMap< String, String > = into_bmap!{ "name" => "value" }; -/// -/// // With trailing comma -/// let map3 : BTreeMap< i32, &str > = into_bmap!( 1 => "one", 2 => "two", 3 => "three", ); -/// ``` -/// -/// # Parameters -/// -/// - `$( $key:expr => $value:expr ),* $( , )?`: A comma-separated list of key-value pairs to insert into the `BTreeMap`. -/// Each key and value can be of any type that implements the `Into< K >` and `Into< V >` traits, where `K` and `V` are the -/// types stored in the `BTreeMap` as keys and values, respectively. -/// -/// # Returns -/// -/// Returns a `BTreeMap` containing all the specified key-value pairs. The map's capacity is -/// automatically determined based on the number of elements provided. -/// -/// # Example -/// -/// Basic usage with string slices and integer values: -/// -/// ```rust -/// # use collection_tools::{ BTreeMap, into_bmap }; -/// let map : BTreeMap< &str, i32 > = into_bmap!( "one" => 1, "two" => 2, "three" => 3 ); -/// assert_eq!( map.get( "one" ), Some( &1 ) ); -/// assert_eq!( map.get( "two" ), Some( &2 ) ); -/// assert_eq!( map.get( "three" ), Some( &3 ) ); -/// ``` -/// -/// # Example -/// -/// Using with different types that implement `Into< K >` and `Into< V >`: -/// -/// ```rust -/// # use collection_tools::{ BTreeMap, into_bmap }; -/// let months : BTreeMap< String, i32 > = into_bmap!( "January" => 1, "February" => 2, "March" => 3 ); -/// assert_eq!( months.get( &"January".to_string() ), Some( &1 ) ); -/// assert_eq!( months.get( &"February".to_string() ), Some( &2 ) ); -/// ``` -/// -/// # Example -/// -/// Creating a `BTreeMap` of integers to strings from literals: -/// -/// ```rust -/// # use collection_tools::{ BTreeMap, into_bmap }; -/// let numbers : BTreeMap< i32, String > = into_bmap!( 1 => "one", 2 => "two", 3 => "three" ); -/// assert_eq!( numbers.get( &1 ), Some( &"one".to_string() ) ); -/// assert_eq!( numbers.get( &2 ), Some( &"two".to_string() ) ); -/// assert_eq!( numbers.get( &3 ), Some( &"three".to_string() ) ); -/// ``` -/// -#[ macro_export( local_inner_macros ) ] -macro_rules! into_bmap -{ - ( - $( $key : expr => $value : expr ),* $( , )? - ) - => - {{ - let mut _map = collection_tools::BTreeMap::new(); - $( - let _ = _map.insert( Into::into( $key ), Into::into( $value ) ); - )* - _map - }}; -} - -/// Creates a `BTreeSet` from a list of elements. -/// -/// The `into_bset` macro allows for convenient creation of a `BTreeSet` with initial elements. -/// Elements passed to the macro are automatically converted into the set's element type -/// using `.into()`, facilitating the use of literals or values of different, but convertible types. -/// -/// Note: The `into_bset` macro relies on the `.into()` method to convert each element into the target type -/// of the `BTreeSet`. This means that the elements must be compatible with the `Into` trait for the -/// type `T` used in the `BTreeSet`. Also, this means that sometimes you must specify the type of collection's items. -/// -/// # Origin -/// -/// This collection is reexported from `alloc`. -/// -/// # Syntax -/// -/// The macro can be called with a comma-separated list of elements. A trailing comma is optional. -/// -/// ```rust -/// # use collection_tools::{ BTreeSet, into_bset }; -/// // BTreeSet of &str -/// let set1 : BTreeSet< &str > = into_bset!( "a", "b", "c" ); -/// -/// // BTreeSet of String -/// let set2 : BTreeSet< String > = into_bset!{ "a".to_string(), "b", "c" }; -/// -/// // With trailing comma -/// let set3 : BTreeSet< i32 > = into_bset!( 1, 2, 3, ); -/// ``` -/// -/// # Parameters -/// -/// - `$( $key:expr ),* $( , )?`: A comma-separated list of elements to insert into the `BTreeSet`. -/// Each element can be of any type that implements the `Into` trait, where `T` is the -/// type stored in the `BTreeSet`. -/// -/// # Returns -/// -/// Returns a `BTreeSet` containing all the specified elements. The capacity of the set is -/// automatically determined based on the number of elements provided. -/// -/// # Example -/// -/// Basic usage with string slices: -/// -/// ```rust -/// # use collection_tools::{ BTreeSet, into_bset }; -/// let set : BTreeSet< &str > = into_bset!( "one", "two", "three" ); -/// assert!( set.contains( "one" ) ); -/// assert!( set.contains( "two" ) ); -/// assert!( set.contains( "three" ) ); -/// assert_eq!( set.len(), 3 ); -/// ``` -/// -/// # Example -/// -/// Using with different types that implement `Into`: -/// -/// ```rust -/// # use collection_tools::{ BTreeSet, into_bset }; -/// let numbers : BTreeSet< i32 > = into_bset!( 1, 2, 3 ); -/// assert!( numbers.contains( &1 ) ); -/// assert!( numbers.contains( &2 ) ); -/// assert!( numbers.contains( &3 ) ); -/// ``` -/// -/// # Example -/// -/// Creating a `BTreeSet` of `String` from string literals: -/// -/// ```rust -/// # use collection_tools::{ BTreeSet, into_bset }; -/// let s : BTreeSet< String > = into_bset!{ "value" }; -/// assert!( s.contains( "value" ) ); -/// ``` -/// -#[ macro_export( local_inner_macros ) ] -macro_rules! into_bset -{ - ( - $( $key : expr ),* $( , )? - ) - => - {{ - let mut _set = collection_tools::BTreeSet::new(); - $( - _set.insert( Into::into( $key ) ); - )* - _set - }}; -} - -/// Creates a `BinaryHeap` from a list of elements. -/// -/// The `into_heap` macro simplifies the creation of a `BinaryHeap` with initial elements. -/// Elements passed to the macro are automatically converted into the heap's element type -/// using `.into()`, allowing for the use of literals or values of different, but convertible types. -/// -/// Note: The `into_heap` macro utilizes the `.into()` method to convert each element into the target type -/// of the `BinaryHeap`. This means that the elements must be compatible with the `Into` trait for the -/// type `T` used in the `BinaryHeap`. Also, this means that sometimes you must specify the type of collection's items. -/// -/// # Origin -/// -/// This collection is reexported from `alloc`. -/// -/// # Syntax -/// -/// The macro can be called with a comma-separated list of elements. A trailing comma is optional. -/// -/// ```rust -/// # use collection_tools::{ BinaryHeap, into_heap }; -/// // BinaryHeap of i32 -/// let heap1 : BinaryHeap< i32 > = into_heap!( 3, 1, 4, 1, 5, 9 ); -/// -/// // BinaryHeap of String -/// let heap2 : BinaryHeap< String > = into_heap!{ "pear".to_string(), "apple", "banana" }; -/// -/// // With trailing comma -/// let heap3 : BinaryHeap< i32 > = into_heap!( 2, 7, 1, 8, ); -/// ``` -/// -/// # Parameters -/// -/// - `$( $key:expr ),* $( , )?`: A comma-separated list of elements to insert into the `BinaryHeap`. -/// Each element can be of any type that implements the `Into` trait, where `T` is the -/// type stored in the `BinaryHeap`. -/// -/// # Returns -/// -/// Returns a `BinaryHeap` containing all the specified elements. The capacity of the heap is -/// automatically determined based on the number of elements provided. -/// -/// # Example -/// -/// Basic usage with integers: -/// -/// ```rust -/// # use collection_tools::{ BinaryHeap, into_heap }; -/// let heap : BinaryHeap< i32 > = into_heap!( 5, 3, 7, 1 ); -/// assert_eq!( heap.peek(), Some( &7 ) ); // The largest value is at the top of the heap -/// ``` -/// -/// # Example -/// -/// Using with different types that implement `Into`: -/// -/// ```rust -/// # use collection_tools::{ BinaryHeap, into_heap }; -/// let chars : BinaryHeap< char > = into_heap!( 'a', 'b', 'c' ); -/// assert_eq!( chars.peek(), Some( &'c' ) ); // Characters are ordered by their ASCII value -/// ``` -/// -/// # Example -/// -/// Creating a `BinaryHeap` of `String` from string literals: -/// -/// ```rust -/// # use collection_tools::{ BinaryHeap, into_heap }; -/// let fruits : BinaryHeap< String > = into_heap!{ "cherry", "apple", "banana" }; -/// assert_eq!( fruits.peek(), Some( &"cherry".to_string() ) ); // The lexicographically largest value is at the top -/// ``` -/// -#[ macro_export( local_inner_macros ) ] -macro_rules! into_heap -{ - ( - $( $key : expr ),* $( , )? - ) - => - {{ - let _cap = count!( @count $( $key ),* ); - let mut _heap = collection_tools::BinaryHeap::with_capacity( _cap ); - $( - _heap.push( Into::into( $key ) ); - )* - _heap - }}; -} - -/// Creates a `HashMap` from a list of key-value pairs. -/// -/// The `into_hmap` macro allows for convenient creation of a `HashMap` with initial elements. -/// Keys and values passed to the macro are automatically converted into the map's key and value types -/// using `.into()`, enabling the use of literals or values of different, but convertible types. -/// -/// Note: The `into_hmap` macro relies on the `.into()` method to convert each key and value into the target types -/// of the `HashMap`. This means that the keys and values must be compatible with the `Into` and `Into` traits -/// for the key type `K` and value type `V` used in the `HashMap`. Also, this means that sometimes you must specify the type of collection's items. -/// -/// # 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 -/// -/// # Syntax -/// -/// The macro can be called with a comma-separated list of key-value pairs. A trailing comma is optional. -/// -/// ```rust -/// # use collection_tools::{ HashMap, into_hmap }; -/// // HashMap of &str to i32 -/// let map1 : HashMap< &str, i32 > = into_hmap!( "one" => 1, "two" => 2, "three" => 3 ); -/// -/// // HashMap of String to String -/// let map2 : HashMap< String, String > = into_hmap!{ "name".to_string() => "value".to_string(), "type" => "example" }; -/// -/// // With trailing comma -/// let map3 : HashMap< i32, &str > = into_hmap!( 1 => "one", 2 => "two", 3 => "three", ); -/// ``` -/// -/// # Parameters -/// -/// - `$( $key:expr => $value:expr ),* $( , )?`: A comma-separated list of key-value pairs to insert into the `HashMap`. -/// Each key and value can be of any type that implements the `Into` and `Into` traits, where `K` and `V` are the -/// types stored in the `HashMap` as keys and values, respectively. -/// -/// # Returns -/// -/// Returns a `HashMap` containing all the specified key-value pairs. The capacity of the map is -/// automatically determined based on the number of elements provided. -/// -/// # Example -/// -/// Basic usage with string slices and integer values: -/// -/// ```rust -/// # use collection_tools::{ HashMap, into_hmap }; -/// let map : HashMap< &str, i32 > = into_hmap!( "one" => 1, "two" => 2, "three" => 3 ); -/// assert_eq!( map.get( "one" ), Some( &1 ) ); -/// assert_eq!( map.get( "two" ), Some( &2 ) ); -/// assert_eq!( map.get( "three" ), Some( &3 ) ); -/// ``` -/// -/// # Example -/// -/// Using with different types that implement `Into` and `Into`: -/// -/// ```rust -/// # use collection_tools::{ HashMap, into_hmap }; -/// let items : HashMap< String, i32 > = into_hmap!( "pen" => 10, "book" => 45, "eraser" => 5 ); -/// assert_eq!( items.get( &"pen".to_string() ), Some(&10 ) ); -/// assert_eq!( items.get( &"book".to_string() ), Some(&45 ) ); -/// ``` -/// -/// # Example -/// -/// Creating a `HashMap` of integers to strings from literals: -/// -/// ```rust -/// # use collection_tools::{ HashMap, into_hmap }; -/// let pairs : HashMap< i32, String > = into_hmap!( 1 => "apple", 2 => "banana" ); -/// assert_eq!( pairs.get( &1 ), Some( &"apple".to_string() ) ); -/// assert_eq!( pairs.get( &2 ), Some( &"banana".to_string() ) ); -/// ``` -/// -#[macro_export(local_inner_macros)] -macro_rules! into_hmap -{ - ( - $( $key : expr => $value : expr ),* $( , )? - ) - => - {{ - let _cap = count!( @count $( $key ),* ); - let mut _map = collection_tools::HashMap::with_capacity( _cap ); - $( - let _ = _map.insert( Into::into( $key ), Into::into( $value ) ); - )* - _map - }}; -} - -/// Creates a `HashSet` from a list of elements. -/// -/// The `into_hset` macro allows for convenient creation of a `HashSet` with initial elements. -/// Elements passed to the macro are automatically converted into the set's element type -/// using `.into()`, facilitating the use of literals or values of different, but convertible types. -/// -/// Note: The `into_hset` macro relies on the `.into()` method to convert each element into the target type -/// of the `HashSet`. This means that the elements must be compatible with the `Into< T >` trait for the -/// type `T` used in the `HashSet`. Also, this means that sometimes you must specify the type of collection's items. -/// -/// # 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 -/// -/// # Syntax -/// -/// The macro can be called with a comma-separated list of elements. A trailing comma is optional. -/// -/// ```rust -/// # use collection_tools::{ HashSet, into_hset }; -/// // HashSet of &str -/// let set1 : HashSet< &str > = into_hset!( "a", "b", "c" ); -/// -/// // HashSet of String -/// let set2 : HashSet< String > = into_hset!{ "a".to_string(), "b", "c" }; -/// -/// // With trailing comma -/// let set3 : HashSet< i32 > = into_hset!( 1, 2, 3, ); -/// ``` -/// -/// # Parameters -/// -/// - `$( $key:expr ),* $( , )?`: A comma-separated list of elements to insert into the `HashSet`. -/// Each element can be of any type that implements the `Into< T >` trait, where `T` is the -/// type stored in the `HashSet`. -/// -/// # Returns -/// -/// Returns a `HashSet` containing all the specified elements. The capacity of the set is -/// automatically determined based on the number of elements provided. -/// -/// # Example -/// -/// Basic usage with string slices: -/// -/// ```rust -/// # use collection_tools::{ HashSet, into_hset }; -/// let set : HashSet< &str > = into_hset!( "one", "two", "three" ); -/// assert!( set.contains( "one" ) ); -/// assert!( set.contains( "two" ) ); -/// assert!( set.contains( "three" ) ); -/// assert_eq!( set.len(), 3 ); -/// ``` -/// -/// # Example -/// -/// Using with different types that implement `Into< T >`: -/// -/// ```rust -/// # use collection_tools::{ HashSet, into_hset }; -/// let numbers : HashSet< i32 > = into_hset!( 1, 2, 3 ); -/// assert!( numbers.contains( &1 ) ); -/// assert!( numbers.contains( &2 ) ); -/// assert!( numbers.contains( &3 ) ); -/// ``` -/// -/// # Example -/// -/// Creating a `HashSet` of `String` from string literals: -/// -/// ```rust -/// # use collection_tools::{ HashSet, into_hset }; -/// let s : HashSet< String > = into_hset!{ "value" }; -/// assert_eq!( s.get( "value" ), Some( &"value".to_string() ) ); -/// ``` -/// -#[ macro_export( local_inner_macros ) ] -macro_rules! into_hset -{ - ( - $( $key : expr ),* $( , )? - ) - => - {{ - let _cap = count!( @count $( $key ),* ); - let mut _set = collection_tools::HashSet::with_capacity( _cap ); - $( - let _ = _set.insert( Into::into( $key ) ); - )* - _set - }}; -} - -/// Creates a `LinkedList` from a list of elements. -/// -/// The `into_list` macro facilitates the creation of a `LinkedList` with initial elements. -/// Elements passed to the macro are automatically converted into the list's element type -/// using `.into()`, making it convenient to use literals or values of different, but convertible types. -/// -/// Note: The `into_list` macro leverages the `.into()` method to convert each element into the target type -/// of the `LinkedList`. Therefore, the elements must be compatible with the `Into` trait for the -/// type `T` used in the `LinkedList`. Also, this means that sometimes you must specify the type of collection's items. -/// -/// # Origin -/// -/// This collection is reexported from `alloc`. -/// -/// # Syntax -/// -/// The macro can be called with a comma-separated list of elements. A trailing comma is optional. -/// -/// ```rust -/// # use collection_tools::{ LinkedList, into_list }; -/// // LinkedList of i32 -/// let lst1 : LinkedList< i32 > = into_list!( 1, 2, 3, 4, 5 ); -/// -/// // LinkedList of String -/// let lst2 : LinkedList< String > = into_list!{ "hello".to_string(), "world", "rust" }; -/// -/// // With trailing comma -/// let lst3 : LinkedList< f64 > = into_list!( 1.1, 2.2, 3.3, ); -/// ``` -/// -/// # Parameters -/// -/// - `$( $key:expr ),* $( , )?`: A comma-separated list of elements to insert into the `LinkedList`. -/// Each element can be of any type that implements the `Into` trait, where `T` is the -/// type stored in the `LinkedList`. -/// -/// # Returns -/// -/// Returns a `LinkedList` containing all the specified elements. The capacity of the list is -/// dynamically adjusted based on the number of elements provided. -/// -/// # Example -/// -/// Basic usage with integers: -/// -/// ```rust -/// # use collection_tools::{ LinkedList, into_list }; -/// let lst: LinkedList< i32 > = into_list!( 1, 2, 3 ); -/// assert_eq!( lst.front(), Some( &1 ) ); // The first element is 1 -/// assert_eq!( lst.back(), Some( &3 ) ); // The last element is 3 -/// ``` -/// -/// # Example -/// -/// Using with different types that implement `Into`: -/// -/// ```rust -/// # use collection_tools::{ LinkedList, into_list }; -/// let chars : LinkedList< String > = into_list!( "a", "b", "c" ); -/// assert!( chars.contains( &"a".to_string() ) ); -/// assert!( chars.contains( &"b".to_string() ) ); -/// assert!( chars.contains( &"c".to_string() ) ); -/// ``` -/// -/// # Example -/// -/// Creating a `LinkedList` of `String` from string literals: -/// -/// ```rust -/// # use collection_tools::{ LinkedList, into_list }; -/// let fruits : LinkedList< String > = into_list!{ "apple", "banana", "cherry" }; -/// assert_eq!( fruits.front(), Some( &"apple".to_string() ) ); // The first element -/// assert_eq!( fruits.back(), Some( &"cherry".to_string() ) ); // The last element -/// ``` -/// -#[ macro_export( local_inner_macros ) ] -macro_rules! into_list -{ - ( - $( $key : expr ),* $( , )? - ) - => - {{ - // "The LinkedList allows pushing and popping elements at either end in constant time." - // So no `with_capacity` - let mut _lst = collection_tools::LinkedList::new(); - $( - _lst.push_back( Into::into( $key ) ); - )* - _lst - }}; -} - -/// Creates a `Vec` from a list of elements. -/// -/// The `into_vec!` macro simplifies the creation of a `Vec` with initial elements. -/// Elements passed to the macro are automatically converted into the vector's element type -/// using `.into()`, making it convenient to use literals or values of different, but convertible types. -/// -/// Note: The `into_vec!` macro utilizes the `.into()` method to convert each element into the target type -/// of the `Vec`. Therefore, the elements must be compatible with the `Into` trait for the -/// type `T` used in the `Vec`. Also, this means that sometimes you must specify the type of collection's items. -/// -/// # Origin -/// -/// This collection is reexported from `alloc`. -/// -/// # Syntax -/// -/// The macro can be called with a comma-separated list of elements. A trailing comma is optional. -/// -/// ```rust -/// # use collection_tools::{Vec, into_vec}; -/// // Vec of i32 -/// let vec1 : Vec< i32 > = into_vec!( 1, 2, 3, 4, 5 ); -/// -/// // Vec of String -/// let vec2 : Vec< String > = into_vec!{ "hello", "world", "rust" }; -/// -/// // With trailing comma -/// let vec3 : Vec< f64 > = into_vec!( 1.1, 2.2, 3.3, ); -/// ``` -/// -/// # Parameters -/// -/// - `$( $key : expr ),* $( , )?`: A comma-separated list of elements to insert into the `Vec`. -/// Each element can be of any type that implements the `Into` trait, where `T` is the -/// type stored in the `Vec`. -/// -/// # Returns -/// -/// Returns a `Vec` containing all the specified elements. The capacity of the vector is -/// automatically determined based on the number of elements provided. -/// -/// # Example -/// -/// Basic usage with integers: -/// -/// ```rust -/// # use collection_tools::{Vec, into_vec}; -/// let vec : Vec< i32 > = into_vec!( 1, 2, 3 ); -/// assert_eq!( vec[ 0 ], 1 ); -/// assert_eq!( vec[ 1 ], 2 ); -/// assert_eq!( vec[ 2 ], 3 ); -/// ``` -/// -/// # Example -/// -/// Using with different types that implement `Into`: -/// -/// ```rust -/// # use collection_tools::{Vec, into_vec}; -/// let words : Vec< String > = into_vec!( "alpha", "beta", "gamma" ); -/// assert_eq!( words[ 0 ], "alpha" ); -/// assert_eq!( words[ 1 ], "beta" ); -/// assert_eq!( words[ 2 ], "gamma" ); -/// ``` -/// -/// # Example -/// -/// Creating a `Vec` of `String` from string literals and String objects: -/// -/// ```rust -/// # use collection_tools::{Vec, into_vec}; -/// let mixed : Vec< String > = into_vec!{ "value", "another value".to_string() }; -/// assert_eq!( mixed[ 0 ], "value" ); -/// assert_eq!( mixed[ 1 ], "another value" ); -/// ``` -/// -#[ macro_export( local_inner_macros ) ] -macro_rules! into_vec -{ - ( - $( $key : expr ),* $( , )? - ) - => - {{ - let _cap = count!( @count $( $key ),* ); - let mut _vec = collection_tools::Vec::with_capacity( _cap ); - $( - _vec.push( Into::into( $key ) ); - )* - _vec - }}; -} - -/// Creates a `VecDeque` from a list of elements. -/// -/// The `into_vecd` macro allows for the convenient creation of a `VecDeque` with initial elements. -/// Elements passed to the macro are automatically converted into the deque's element type -/// using `.into()`, enabling the use of literals or values of different, but convertible types. -/// -/// Note: The `into_vecd` macro relies on the `.into()` method to convert each element into the target type -/// of the `VecDeque`. This means that the elements must be compatible with the `Into` trait for the -/// type `T` used in the `VecDeque`. -/// -/// # Origin -/// -/// This collection is reexported from `alloc`. -/// -/// # Syntax -/// -/// The macro can be called with a comma-separated list of elements. A trailing comma is optional. -/// -/// ```rust -/// # use collection_tools::{ VecDeque, into_vecd }; -/// // VecDeque of i32 -/// let vd1 : VecDeque< i32 > = into_vecd!( 1, 2, 3, 4, 5 ); -/// -/// // VecDeque of String -/// let vd2 : VecDeque< String > = into_vecd!{ "hello".to_string(), "world", "rust" }; -/// -/// // With trailing comma -/// let vd3 : VecDeque< f64 > = into_vecd!( 1.1, 2.2, 3.3, ); -/// ``` -/// -/// # Parameters -/// -/// - `$( $key:expr ),* $( , )?`: A comma-separated list of elements to insert into the `VecDeque`. -/// Each element can be of any type that implements the `Into< T >` trait, where `T` is the -/// type stored in the `VecDeque`. -/// -/// # Returns -/// -/// Returns a `VecDeque` containing all the specified elements. The capacity of the deque is -/// automatically determined based on the number of elements provided. -/// -/// # Example -/// -/// Basic usage with integers: -/// -/// ```rust -/// # use collection_tools::{ VecDeque, into_vecd }; -/// let vd : VecDeque< i32 > = into_vecd!( 1, 2, 3 ); -/// assert_eq!( vd.front(), Some( &1 ) ); // The first element is 1 -/// assert_eq!( vd.back(), Some( &3 ) ); // The last element is 3 -/// ``` -/// -/// # Example -/// -/// Using with different types that implement `Into< T >`: -/// -/// ```rust -/// # use collection_tools::{ VecDeque, into_vecd }; -/// let chars : VecDeque< char > = into_vecd!( 'a', 'b', 'c' ); -/// assert!( chars.contains( &'a' ) ); -/// assert!( chars.contains( &'b' ) ); -/// assert!( chars.contains( &'c' ) ); -/// ``` -/// -/// # Example -/// -/// Creating a `VecDeque` of `String` from string literals: -/// -/// ```rust -/// # use collection_tools::{ VecDeque, into_vecd }; -/// let fruits : VecDeque< String > = into_vecd!{ "apple", "banana", "cherry" }; -/// assert_eq!( fruits.front(), Some( &"apple".to_string() ) ); // The first element -/// assert_eq!( fruits.back(), Some( &"cherry".to_string() ) ); // The last element -/// ``` -/// -#[ macro_export( local_inner_macros ) ] -macro_rules! into_vecd -{ - ( - $( $key : expr ),* $( , )? - ) - => - {{ - let _cap = count!( @count $( $key ),* ); - let mut _vecd = collection_tools::VecDeque::with_capacity( _cap ); - $( - _vecd.push_back( Into::into( $key ) ); - )* - _vecd - }}; -} diff --git a/module/core/collection_tools/src/lib.rs b/module/core/collection_tools/src/lib.rs index 220d3689a6..6171c0e365 100644 --- a/module/core/collection_tools/src/lib.rs +++ b/module/core/collection_tools/src/lib.rs @@ -4,34 +4,17 @@ #![ 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; +#[ cfg( feature = "enabled" ) ] +#[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] +extern crate alloc; -/// 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; +/// Module containing all collection macros +#[ cfg( feature = "enabled" ) ] +#[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] +mod collections; +#[ cfg( feature = "enabled" ) ] +#[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] +pub use collections::*; /// Namespace with dependencies. #[ cfg( feature = "enabled" ) ] @@ -57,30 +40,6 @@ 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. @@ -96,21 +55,44 @@ pub mod orphan #[ cfg( feature = "enabled" ) ] pub mod exposed { + #[ doc( inline ) ] #[ allow( unused_imports ) ] pub use super::prelude::*; + + // #[ cfg( feature = "reexports" ) ] + #[ cfg( any( feature = "use_alloc", not( feature = "no_std" ) ) ) ] + #[ doc( inline ) ] + #[ allow( unused_imports ) ] + pub use crate:: + { + bmap::BTreeMap, + bset::BTreeSet, + heap::BinaryHeap, + hmap::HashMap, + hset::HashSet, + list::LinkedList, + vec::Vec, + vecd::VecDeque, + }; + + // #[ cfg( feature = "reexports" ) ] + #[ cfg( any( feature = "use_alloc", not( feature = "no_std" ) ) ) ] + #[ doc( inline ) ] + #[ allow( unused_imports ) ] + pub use + { + HashMap as Map, + HashSet as Set, + Vec as DynArray, + }; + + // qqq : cover by tests presence of all containers immidiately in collection_tools::* and in collection_tools::exposed::* + } /// Prelude to use essentials: `use my_module::prelude::*`. #[ cfg( feature = "enabled" ) ] pub mod prelude { - #[ cfg( feature = "collection_constructors" ) ] - #[ doc( inline ) ] - #[ allow( unused_imports ) ] - pub use super::constructors::*; - #[ cfg( feature = "collection_into_constructors" ) ] - #[ doc( inline ) ] - #[ allow( unused_imports ) ] - pub use super::into_constructors::*; } diff --git a/module/core/collection_tools/src/vec.rs b/module/core/collection_tools/src/vec.rs deleted file mode 100644 index f4e6502089..0000000000 --- a/module/core/collection_tools/src/vec.rs +++ /dev/null @@ -1,2 +0,0 @@ - -pub use core::slice::Iter diff --git a/module/core/collection_tools/tests/inc/bmap.rs b/module/core/collection_tools/tests/inc/bmap.rs new file mode 100644 index 0000000000..f9034314aa --- /dev/null +++ b/module/core/collection_tools/tests/inc/bmap.rs @@ -0,0 +1,94 @@ +use super::*; + +#[ test ] +fn reexport() +{ + + 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 ); + +} + +#[ cfg( feature = "collection_constructors" ) ] +#[ test ] +fn constructor() +{ + + // 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( feature = "collection_into_constructors" ) ] +#[ test ] +fn into_constructor() +{ + + // test.case( "empty" ); + let got : the_module::BTreeMap< i32, i32 > = the_module::into_bmap!{}; + let exp = the_module::BTreeMap::new(); + assert_eq!( got, exp ); + + // test.case( "multiple entry" ); + let got = the_module::into_bmap!{ 3 => 13, 4 => 1 }; + let mut exp = the_module::BTreeMap::new(); + exp.insert(3, 13); + exp.insert(4, 1); + assert_eq!( got, exp ); + +} + +#[ test ] +fn iters() +{ + + struct MyContainer + { + entries : the_module::BTreeMap< i32, i32 >, + } + + impl IntoIterator for MyContainer + { + type Item = ( i32, i32 ); + type IntoIter = the_module::bmap::IntoIter< i32, i32 >; + + fn into_iter( self ) -> Self::IntoIter + { + self.entries.into_iter() + } + } + + impl< 'a > IntoIterator for &'a MyContainer + { + type Item = ( &'a i32, &'a i32 ); + type IntoIter = the_module::bmap::Iter< 'a, i32, i32 >; + + fn into_iter( self ) -> Self::IntoIter + { + self.entries.iter() + } + } + + let instance = MyContainer { entries : the_module::BTreeMap::from( [ ( 1, 3 ), ( 2, 2 ), ( 3, 1 ) ] ) }; + let got : the_module::BTreeMap< _, _ > = instance.into_iter().collect(); + let exp = the_module::BTreeMap::from( [ ( 1, 3 ), ( 2, 2 ), ( 3, 1 ) ] ); + a_id!( got, exp ); + + let instance = MyContainer { entries : the_module::BTreeMap::from( [ ( 1, 3 ), ( 2, 2 ), ( 3, 1 ) ] ) }; + let got : the_module::BTreeMap< _, _ > = ( &instance ).into_iter().map( | ( k, v ) | ( k.clone(), v.clone() ) ).collect(); + let exp = the_module::BTreeMap::from( [ ( 1, 3 ), ( 2, 2 ), ( 3, 1 ) ] ); + a_id!( got, exp ); + +} diff --git a/module/core/collection_tools/tests/inc/bset.rs b/module/core/collection_tools/tests/inc/bset.rs new file mode 100644 index 0000000000..b08f7593c4 --- /dev/null +++ b/module/core/collection_tools/tests/inc/bset.rs @@ -0,0 +1,93 @@ +use super::*; + +#[ test ] +fn reexport() +{ + + 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 ); + +} + +#[ cfg( feature = "collection_constructors" ) ] +#[ test ] +fn constructor() +{ + + // 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( feature = "collection_into_constructors" ) ] +#[ test ] +fn into_constructor() +{ + + // test.case( "empty" ); + let got : the_module::BTreeSet< i32 > = the_module::into_bset!{}; + let exp = the_module::BTreeSet::new(); + assert_eq!( got, exp ); + + // test.case( "multiple 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 ); + +} + +#[ test ] +fn iters() +{ + + struct MyContainer + { + entries : the_module::BTreeSet< i32 >, + } + + impl IntoIterator for MyContainer + { + type Item = i32; + type IntoIter = the_module::bset::IntoIter< i32 >; + + fn into_iter( self ) -> Self::IntoIter + { + self.entries.into_iter() + } + } + + impl< 'a > IntoIterator for &'a MyContainer + { + type Item = &'a i32; + type IntoIter = the_module::bset::Iter< 'a, i32 >; + + fn into_iter( self ) -> Self::IntoIter + { + self.entries.iter() + } + } + + let instance = MyContainer { entries : the_module::BTreeSet::from( [ 1, 2, 3 ] ) }; + let got : the_module::BTreeSet< _ > = instance.into_iter().collect(); + let exp = the_module::BTreeSet::from( [ 1, 2, 3 ] ); + a_id!( got, exp ); + + let instance = MyContainer { entries : the_module::BTreeSet::from( [ 1, 2, 3 ] ) }; + let got : the_module::BTreeSet< _ > = ( &instance ).into_iter().cloned().collect(); + let exp = the_module::BTreeSet::from( [ 1, 2, 3 ] ); + a_id!( got, exp ); + +} diff --git a/module/core/collection_tools/tests/inc/components.rs b/module/core/collection_tools/tests/inc/components.rs index ee19c86a6c..e2503addb7 100644 --- a/module/core/collection_tools/tests/inc/components.rs +++ b/module/core/collection_tools/tests/inc/components.rs @@ -1,55 +1,4 @@ #[ 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 deleted file mode 100644 index dda241a1a4..0000000000 --- a/module/core/collection_tools/tests/inc/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< i32 > = 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::< i32 >::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/inc/heap.rs b/module/core/collection_tools/tests/inc/heap.rs new file mode 100644 index 0000000000..a342548cfc --- /dev/null +++ b/module/core/collection_tools/tests/inc/heap.rs @@ -0,0 +1,94 @@ +use super::*; + +#[ test ] +fn reexport() +{ + + 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 ); + +} + +#[ cfg( feature = "collection_constructors" ) ] +#[ test ] +fn constructor() +{ + + // test.case( "empty" ); + let got : the_module::BinaryHeap< i32 > = the_module::heap!{}; + let exp: the_module::BinaryHeap< i32 > = 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( feature = "collection_into_constructors" ) ] +#[ test ] +fn into_constructor() +{ + + // test.case( "empty" ); + let got : the_module::BinaryHeap< i32 > = the_module::into_heap!{}; + let exp = the_module::BinaryHeap::< i32 >::new(); + assert_eq!( got.into_vec(), exp.into_vec() ); + + // test.case( "multiple 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() ); + +} + +#[ test ] +fn iters() +{ + + struct MyContainer + { + entries : the_module::BinaryHeap< i32 >, + } + + impl IntoIterator for MyContainer + { + type Item = i32; + type IntoIter = the_module::heap::IntoIter< i32 >; + + fn into_iter( self ) -> Self::IntoIter + { + self.entries.into_iter() + } + } + + impl< 'a > IntoIterator for &'a MyContainer + { + type Item = &'a i32; + type IntoIter = the_module::heap::Iter< 'a, i32 >; + + fn into_iter( self ) -> Self::IntoIter + { + self.entries.iter() + } + } + + let instance = MyContainer { entries : the_module::BinaryHeap::from( [ 1, 2, 3 ] ) }; + let got : the_module::BinaryHeap< i32 > = instance.into_iter().collect(); + let exp : the_module::BinaryHeap< i32 > = the_module::BinaryHeap::from( [ 1, 2, 3 ] ); + a_id!( got.into_sorted_vec(), exp.into_sorted_vec() ); + + let instance = MyContainer { entries : the_module::BinaryHeap::from( [ 1, 2, 3 ] ) }; + let got : the_module::BinaryHeap< i32 > = ( &instance ).into_iter().cloned().collect(); + let exp : the_module::BinaryHeap< i32 > = the_module::BinaryHeap::from( [ 1, 2, 3 ] ); + a_id!( got.into_sorted_vec(), exp.into_sorted_vec() ); + +} diff --git a/module/core/collection_tools/tests/inc/hmap.rs b/module/core/collection_tools/tests/inc/hmap.rs new file mode 100644 index 0000000000..1d5d0027d1 --- /dev/null +++ b/module/core/collection_tools/tests/inc/hmap.rs @@ -0,0 +1,120 @@ +use super::*; + +#[ test ] +fn reexport() +{ + + let mut map1 : the_module::HashMap< i32, i32 > = the_module::HashMap::new(); + map1.insert( 1, 2 ); + let exp = 2; + let got = *map1.get( &1 ).unwrap(); + assert_eq!( exp, got ); + + let mut map2 : the_module::Map< i32, i32 > = the_module::Map::new(); + map2.insert( 1, 2 ); + let exp = 2; + let got = *map2.get( &1 ).unwrap(); + assert_eq!( exp, got ); + + assert_eq!( map1, map2 ); + +} + +#[ cfg( feature = "collection_constructors" ) ] +#[ test ] +fn constructor() +{ + + // 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( feature = "collection_into_constructors" ) ] +#[ test ] +fn into_constructor() +{ + + // 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( "multiple entry" ); + let got = the_module::into_hmap!{ 3 => 13, 4 => 1 }; + let mut exp = the_module::HashMap::new(); + exp.insert( 3, 13 ); + exp.insert( 4, 1 ); + assert_eq!( got, exp ); + +} + +#[ test ] +fn iters() +{ + + struct MyContainer + { + entries : the_module::HashMap< i32, i32 >, + } + + impl IntoIterator for MyContainer + { + type Item = ( i32, i32 ); + type IntoIter = the_module::hmap::IntoIter< i32, i32 >; + + fn into_iter( self ) -> Self::IntoIter + { + self.entries.into_iter() + } + } + + impl< 'a > IntoIterator for &'a MyContainer + { + type Item = ( &'a i32, &'a i32 ); + type IntoIter = the_module::hmap::Iter< 'a, i32, i32 >; + + fn into_iter( self ) -> Self::IntoIter + { + self.entries.iter() + } + } + + impl< 'a > IntoIterator for &'a mut MyContainer + { + type Item = ( &'a i32, &'a mut i32 ); + type IntoIter = the_module::hmap::IterMut< 'a, i32, i32 >; + + fn into_iter( self ) -> Self::IntoIter + { + self.entries.iter_mut() + } + } + + let instance = MyContainer { entries : the_module::HashMap::from( [ ( 1 , 3 ), ( 2, 2 ), ( 3, 1 ) ] ) }; + let got : the_module::HashMap< _, _ > = instance.into_iter().collect(); + let exp = the_module::HashMap::from( [ ( 1 , 3 ), ( 2, 2 ), ( 3, 1 ) ] ); + a_id!( got, exp ); + + let instance = MyContainer { entries : the_module::HashMap::from( [ ( 1 , 3 ), ( 2, 2 ), ( 3, 1 ) ] ) }; + let got : the_module::HashMap< _, _ > = ( &instance ).into_iter().map( | ( k, v ) | ( k.clone(), v.clone() ) ).collect(); + let exp = the_module::HashMap::from( [ ( 1 , 3 ), ( 2, 2 ), ( 3, 1 ) ] ); + a_id!( got, exp ); + + let mut instance = MyContainer { entries : the_module::HashMap::from( [ ( 1 , 3 ), ( 2, 2 ), ( 3, 1 ) ] ) }; + ( &mut instance ).into_iter().for_each( | ( _, v ) | *v *= 2 ); + let exp = the_module::HashMap::from( [ ( 1, 6 ), ( 2 ,4 ), ( 3, 2 ) ] ); + a_id!( instance.entries, exp ); + +} diff --git a/module/core/collection_tools/tests/inc/hset.rs b/module/core/collection_tools/tests/inc/hset.rs new file mode 100644 index 0000000000..d5a0ad5ed9 --- /dev/null +++ b/module/core/collection_tools/tests/inc/hset.rs @@ -0,0 +1,100 @@ +use super::*; + +#[ test ] +fn reexport() +{ + + let mut set1 : the_module::HashSet< i32 > = the_module::HashSet::new(); + set1.insert( 1 ); + assert_eq!( set1.contains( &1 ), true ); + assert_eq!( set1.contains( &2 ), false ); + + let mut set2 : the_module::Set< i32 > = the_module::Set::new(); + set2.insert( 1 ); + assert_eq!( set2.contains( &1 ), true ); + assert_eq!( set2.contains( &2 ), false ); + + assert_eq!( set1, set2 ); + +} + +#[ cfg( feature = "collection_constructors" ) ] +#[ test ] +fn constructor() +{ + + // 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( feature = "collection_into_constructors" ) ] +#[ test ] +fn into_constructor() +{ + + // 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( "multiple entry" ); + let got = the_module::into_hset!{ 13, 11 }; + let mut exp = the_module::HashSet::new(); + exp.insert( 11 ); + exp.insert( 13 ); + assert_eq!( got, exp ); + +} + +#[ test ] +fn iters() +{ + + struct MyContainer + { + entries : the_module::HashSet< i32 >, + } + + impl IntoIterator for MyContainer + { + type Item = i32; + type IntoIter = the_module::hset::IntoIter< i32 >; + + fn into_iter( self ) -> Self::IntoIter + { + self.entries.into_iter() + } + } + + impl< 'a > IntoIterator for &'a MyContainer + { + type Item = &'a i32; + type IntoIter = the_module::hset::Iter< 'a, i32 >; + + fn into_iter( self ) -> Self::IntoIter + { + self.entries.iter() + } + } + + let instance = MyContainer { entries : the_module::HashSet::from( [ 1, 2, 3 ] ) }; + let got : the_module::HashSet< _ > = instance.into_iter().collect(); + let exp = the_module::HashSet::from( [ 1, 2, 3 ] ); + a_id!( got, exp ); + + let instance = MyContainer { entries : the_module::HashSet::from( [ 1, 2, 3 ] ) }; + let got : the_module::HashSet< _ > = ( &instance ).into_iter().cloned().collect(); + let exp = the_module::HashSet::from( [ 1, 2, 3 ] ); + a_id!( got, exp ); + +} diff --git a/module/core/collection_tools/tests/inc/into_constructors.rs b/module/core/collection_tools/tests/inc/into_constructors.rs deleted file mode 100644 index 7423159092..0000000000 --- a/module/core/collection_tools/tests/inc/into_constructors.rs +++ /dev/null @@ -1,173 +0,0 @@ -// xxx : uncomment - -#[ 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::new(); - assert_eq!( got, exp ); - - // test.case( "multiple entry" ); - let got = the_module::into_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::into_bset!{}; - let exp = the_module::BTreeSet::new(); - assert_eq!( got, exp ); - - // test.case( "multiple 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 >::new(); - assert_eq!( got.into_vec(), exp.into_vec() ); - - // test.case( "multiple 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( "multiple entry" ); - let got = the_module::into_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::into_hset!{}; - let exp = the_module::HashSet::new(); - assert_eq!( got, exp ); - - // test.case( "multiple entry" ); - let got = the_module::into_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::into_list!{}; - let exp = the_module::LinkedList::new(); - assert_eq!( got, exp ); - - // test.case( "multiple 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 >::new(); - assert_eq!( got, exp ); - - // test.case( "multiple 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::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/inc/list.rs b/module/core/collection_tools/tests/inc/list.rs new file mode 100644 index 0000000000..648cdf8061 --- /dev/null +++ b/module/core/collection_tools/tests/inc/list.rs @@ -0,0 +1,109 @@ +use super::*; + +#[ test ] +fn reexport() +{ + + 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 ); + +} + +#[ cfg( feature = "collection_constructors" ) ] +#[ test ] +fn constructor() +{ + + // 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( feature = "collection_into_constructors" ) ] +#[ test ] +fn into_constructor() +{ + + // 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( "multiple 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 ); + +} + +#[ test ] +fn iters() +{ + + struct MyContainer + { + entries : the_module::LinkedList< i32 >, + } + + impl IntoIterator for MyContainer + { + type Item = i32; + type IntoIter = the_module::list::IntoIter< i32 >; + + fn into_iter( self ) -> Self::IntoIter + { + self.entries.into_iter() + } + } + + impl< 'a > IntoIterator for &'a MyContainer + { + type Item = &'a i32; + type IntoIter = the_module::list::Iter< 'a, i32 >; + + fn into_iter( self ) -> Self::IntoIter + { + self.entries.iter() + } + } + + impl< 'a > IntoIterator for &'a mut MyContainer + { + type Item = &'a mut i32; + type IntoIter = the_module::list::IterMut< 'a, i32 >; + + fn into_iter( self ) -> Self::IntoIter + { + self.entries.iter_mut() + } + } + + let instance = MyContainer { entries : the_module::LinkedList::from( [ 1, 2, 3 ] ) }; + let got : the_module::LinkedList< _ > = instance.into_iter().collect(); + let exp = the_module::LinkedList::from( [ 1, 2, 3 ] ); + a_id!( got, exp ); + + let instance = MyContainer { entries : the_module::LinkedList::from( [ 1, 2, 3 ] ) }; + let got : the_module::LinkedList< _ > = ( &instance ).into_iter().cloned().collect(); + let exp = the_module::LinkedList::from( [ 1, 2, 3 ] ); + a_id!( got, exp ); + + let mut instance = MyContainer { entries : the_module::LinkedList::from( [ 1, 2, 3 ] ) }; + ( &mut instance ).into_iter().for_each( | v | *v *= 2 ); + let exp = the_module::LinkedList::from( [ 2, 4, 6 ] ); + a_id!( instance.entries, exp ); + +} diff --git a/module/core/collection_tools/tests/inc/mod.rs b/module/core/collection_tools/tests/inc/mod.rs index 00cc188bc4..aa87ee1867 100644 --- a/module/core/collection_tools/tests/inc/mod.rs +++ b/module/core/collection_tools/tests/inc/mod.rs @@ -1,16 +1,15 @@ -#[ allow( unused_imports ) ] use super::*; -#[ cfg( any( feature = "collection_into_constructors") ) ] -mod into_constructors; - -#[ cfg( any( feature = "collection_constructors" ) ) ] -mod constructors; - -#[ cfg( any( feature = "collection_std" ) ) ] -mod reexport; +mod bmap; +mod bset; +mod heap; +mod hmap; +mod hset; +mod list; +mod vec; +mod vecd; mod components; -// qqq : make subdirectory for each container -// qqq : don't put tests otsude of directory `inc` +// qqq : make subdirectory for each container -- done +// qqq : don't put tests otsude of directory `inc` -- done diff --git a/module/core/collection_tools/tests/inc/reexport.rs b/module/core/collection_tools/tests/inc/reexport.rs deleted file mode 100644 index 000c6bc3fd..0000000000 --- a/module/core/collection_tools/tests/inc/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/inc/vec.rs b/module/core/collection_tools/tests/inc/vec.rs new file mode 100644 index 0000000000..ff68b33ec8 --- /dev/null +++ b/module/core/collection_tools/tests/inc/vec.rs @@ -0,0 +1,124 @@ +use super::*; + +#[ test ] +fn reexport() +{ + + let mut vec1 : the_module::Vec< i32 > = the_module::Vec::new(); + vec1.push( 1 ); + vec1.push( 2 ); + let got = vec1.first().unwrap().clone(); + assert_eq!( got, 1 ); + let got = vec1.last().unwrap().clone(); + assert_eq!( got, 2 ); + + let mut vec2 : the_module::DynArray< i32 > = the_module::DynArray::new(); + vec2.push( 1 ); + vec2.push( 2 ); + let got = vec2.first().unwrap().clone(); + assert_eq!( got, 1 ); + let got = vec2.last().unwrap().clone(); + assert_eq!( got, 2 ); + + assert_eq!( vec1, vec2 ); + +} + +#[ cfg( feature = "collection_constructors" ) ] +#[ test ] +fn constructor() +{ + + // test.case( "empty" ); + let got : the_module::Vec< i32 > = the_module::vec!{}; + let exp = the_module::Vec::< i32 >::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( feature = "collection_into_constructors" ) ] +#[ test ] +fn into_constructor() +{ + + // test.case( "empty" ); + let got : the_module::Vec< i32 > = the_module::into_vec!{}; + let exp = the_module::Vec::< i32 >::new(); + assert_eq!( got, exp ); + + // test.case( "multiple 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 ); + +} + +// qqq : implement similar test for all containers -- done +#[ test ] +fn iters() +{ + + struct MyContainer + { + entries : Vec< i32 >, + } + + impl IntoIterator for MyContainer + { + type Item = i32; + type IntoIter = the_module::vec::IntoIter< i32 >; + // qqq : should work -- works + + fn into_iter( self ) -> Self::IntoIter + { + self.entries.into_iter() + } + } + + impl< 'a > IntoIterator for &'a MyContainer + { + type Item = &'a i32; + type IntoIter = the_module::vec::Iter< 'a, i32 >; + + fn into_iter( self ) -> Self::IntoIter + { + self.entries.iter() + } + } + + impl< 'a > IntoIterator for &'a mut MyContainer + { + type Item = &'a mut i32; + type IntoIter = the_module::vec::IterMut< 'a, i32 >; + + fn into_iter( self ) -> Self::IntoIter + { + self.entries.iter_mut() + } + } + + let instance = MyContainer { entries : the_module::Vec::from( [ 1, 2, 3 ] ) }; + let got : Vec< _ > = instance.into_iter().collect(); + let exp = the_module::Vec::from( [ 1, 2, 3 ] ); + a_id!( got, exp ); + + let instance = MyContainer { entries : the_module::Vec::from( [ 1, 2, 3 ] ) }; + let got : Vec< _ > = ( &instance ).into_iter().cloned().collect(); + let exp = the_module::Vec::from( [ 1, 2, 3 ] ); + a_id!( got, exp ); + + let mut instance = MyContainer { entries : the_module::Vec::from( [ 1, 2, 3 ] ) }; + ( &mut instance ).into_iter().for_each( | v | *v *= 2 ); + let exp = the_module::Vec::from( [ 2, 4, 6 ] ); + a_id!( instance.entries, exp ); + +} diff --git a/module/core/collection_tools/tests/inc/vecd.rs b/module/core/collection_tools/tests/inc/vecd.rs new file mode 100644 index 0000000000..5692b56fa9 --- /dev/null +++ b/module/core/collection_tools/tests/inc/vecd.rs @@ -0,0 +1,108 @@ +use super::*; + +#[ test ] +fn reexport() +{ + + 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 ); + +} + +#[ cfg( feature = "collection_constructors" ) ] +#[ test ] +fn constructor() +{ + + // 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 ); + +} + +#[ cfg( feature = "collection_into_constructors" ) ] +#[ test ] +fn into_constructor() +{ + + // 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::into_vecd!{ 3, 13 }; + let mut exp = the_module::VecDeque::new(); + exp.push_front( 13 ); + exp.push_front( 3 ); + assert_eq!( got, exp ); + +} + +#[ test ] +fn iters() +{ + struct MyContainer + { + entries : the_module::VecDeque< i32 >, + } + + impl IntoIterator for MyContainer + { + type Item = i32; + type IntoIter = the_module::vecd::IntoIter< i32 >; + + fn into_iter( self ) -> Self::IntoIter + { + self.entries.into_iter() + } + } + + impl< 'a > IntoIterator for &'a MyContainer + { + type Item = &'a i32; + type IntoIter = the_module::vecd::Iter< 'a, i32 >; + + fn into_iter( self ) -> Self::IntoIter + { + self.entries.iter() + } + } + + impl< 'a > IntoIterator for &'a mut MyContainer + { + type Item = &'a mut i32; + type IntoIter = the_module::vecd::IterMut< 'a, i32 >; + + fn into_iter( self ) -> Self::IntoIter + { + self.entries.iter_mut() + } + } + + let instance = MyContainer { entries : the_module::VecDeque::from( [ 1, 2, 3 ] ) }; + let got : the_module::VecDeque< _ > = instance.into_iter().collect(); + let exp = the_module::VecDeque::from( [ 1, 2, 3 ] ); + a_id!( got, exp ); + + let instance = MyContainer { entries : the_module::VecDeque::from( [ 1, 2, 3 ] ) }; + let got : the_module::VecDeque< _ > = ( &instance ).into_iter().cloned().collect(); + let exp = the_module::VecDeque::from( [ 1, 2, 3 ] ); + a_id!( got, exp ); + + let mut instance = MyContainer { entries : the_module::VecDeque::from( [ 1, 2, 3 ] ) }; + ( &mut instance ).into_iter().for_each( | v | *v *= 2 ); + let exp = the_module::VecDeque::from( [ 2, 4, 6 ] ); + a_id!( instance.entries, exp ); + +} diff --git a/module/core/collection_tools/tests/tests.rs b/module/core/collection_tools/tests/tests.rs index b84a5dc030..a36c5debec 100644 --- a/module/core/collection_tools/tests/tests.rs +++ b/module/core/collection_tools/tests/tests.rs @@ -9,4 +9,6 @@ use test_tools::exposed::*; #[ allow( unused_imports ) ] use ::collection_tools as the_module; +#[ cfg( feature = "enabled" ) ] +#[ cfg( any( feature = "use_alloc", not( feature = "no_std" ) ) ) ] mod inc; diff --git a/module/core/data_type/Cargo.toml b/module/core/data_type/Cargo.toml index dcbfb7b947..cbb30c67f3 100644 --- a/module/core/data_type/Cargo.toml +++ b/module/core/data_type/Cargo.toml @@ -34,6 +34,7 @@ default = [ "dt_either", "dt_prelude", "dt_interval", + "dt_collections", # "dt_make", # "dt_vectorized_from", # "type_constructor/default", @@ -44,6 +45,7 @@ full = [ "dt_either", "dt_prelude", "dt_interval", + "dt_collections", # "dt_make", # "dt_vectorized_from", # "type_constructor/full", @@ -52,9 +54,14 @@ no_std = [] use_alloc = [ "no_std" ] enabled = [] -dt_prelude = [] -dt_either = [ "either" ] +# dt_prelude = [ "collection_tools/reexports" ] +dt_prelude = [] # rid off maybe? dt_interval = [ "interval_adapter/enabled" ] +dt_collections = [ "collection_tools/enabled" ] +dt_either = [ "either" ] + +# qqq : for Anton : integrate all features of collection_tools into data_type and reuse tests + # dt_type_constructor = [ "type_constructor/enabled" ] # dt_make = [ "type_constructor/make" ] # dt_vectorized_from = [ "type_constructor/vectorized_from" ] @@ -85,6 +92,7 @@ either = { version = "~1.6", optional = true } ## internal # type_constructor = { workspace = true } interval_adapter = { workspace = true } +collection_tools = { workspace = true } [dev-dependencies] test_tools = { workspace = true } diff --git a/module/core/data_type/Readme.md b/module/core/data_type/Readme.md index 806e26e4c8..1fccd8d0c7 100644 --- a/module/core/data_type/Readme.md +++ b/module/core/data_type/Readme.md @@ -2,7 +2,7 @@ # Module :: data_type - [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/module_data_type_push.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/module_data_type_push.yml) [![docs.rs](https://img.shields.io/docsrs/data_type?color=e3e8f0&logo=docs.rs)](https://docs.rs/data_type) [![Open in Gitpod](https://raster.shields.io/static/v1?label=try&message=online&color=eee&logo=gitpod&logoColor=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module/core/data_type/examples/data_type_trivial.rs,RUN_POSTFIX=--example%20/home/sakapoi/Документи/wTools_fork/module/core/data_type/examples/data_type_trivial/https://github.com/Wandalen/wTools) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) + [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/module_data_type_push.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/module_data_type_push.yml) [![docs.rs](https://img.shields.io/docsrs/data_type?color=e3e8f0&logo=docs.rs)](https://docs.rs/data_type) [![Open in Gitpod](https://raster.shields.io/static/v1?label=try&message=online&color=eee&logo=gitpod&logoColor=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fcore%2Fdata_type%2Fexamples%2Fdata_type_trivial.rs,RUN_POSTFIX=--example%20data_type_trivial/https://github.com/Wandalen/wTools) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) Collection of primal data types. @@ -46,7 +46,7 @@ Macro [types](https://docs.rs/type_constructor/latest/type_constructor/types/mac ### Basic Use Case :: make - variadic constructor -Implement traits [From_0], [From_1] up to MakeN to provide the interface to construct your structure with a different set of arguments. +Implement traits [From_0], [From1] up to MakeN to provide the interface to construct your structure with a different set of arguments. In this example structure, Struct1 could be constructed either without arguments, with a single argument, or with two arguments. - Constructor without arguments fills fields with zero. - Constructor with a single argument sets both fields to the value of the argument. @@ -74,17 +74,17 @@ In this example structure, Struct1 could be constructed either without arguments } } - impl From_1< i32 > for Struct1 + impl From1< i32 > for Struct1 { - fn from_1( val : i32 ) -> Self + fn from1( val : i32 ) -> Self { Self { a : val, b : val } } } - impl From_2< i32, i32 > for Struct1 + impl From2< i32, i32 > for Struct1 { - fn from_2( val1 : i32, val2 : i32 ) -> Self + fn from2( val1 : i32, val2 : i32 ) -> Self { Self { a : val1, b : val2 } } diff --git a/module/core/data_type/src/dt.rs b/module/core/data_type/src/dt.rs index 23b1d18771..9fb884985f 100644 --- a/module/core/data_type/src/dt.rs +++ b/module/core/data_type/src/dt.rs @@ -3,6 +3,10 @@ pub( crate ) mod private { } +#[ doc( inline ) ] +#[ allow( unused_imports ) ] +pub use protected::*; + /// Protected namespace of the module. pub mod protected { @@ -11,10 +15,6 @@ pub mod protected pub use super::orphan::*; } -#[ doc( inline ) ] -#[ allow( unused_imports ) ] -pub use protected::*; - /// Shared with parent namespace of the module pub mod orphan { @@ -26,34 +26,52 @@ pub mod orphan /// Exposed namespace of the module. pub mod exposed { + #[ doc( inline ) ] #[ allow( unused_imports ) ] pub use super::prelude::*; + #[ cfg( feature = "either" ) ] #[ doc( inline ) ] #[ allow( unused_imports ) ] pub use ::either::Either; - #[ cfg( feature = "type_constructor" ) ] + + // #[ cfg( feature = "type_constructor" ) ] + // #[ doc( inline ) ] + // #[ allow( unused_imports ) ] + // pub use ::type_constructor::exposed::*; + + #[ cfg( feature = "dt_interval" ) ] #[ doc( inline ) ] #[ allow( unused_imports ) ] - pub use ::type_constructor::exposed::*; - #[ cfg( feature = "interval" ) ] + pub use crate::dependency::interval_adapter::exposed::*; + + #[ cfg( feature = "dt_collection" ) ] #[ doc( inline ) ] #[ allow( unused_imports ) ] - pub use ::interval_adapter::exposed::*; + pub use crate::dependency::collection_tools::exposed::*; + } /// Prelude to use essentials: `use my_module::prelude::*`. pub mod prelude { + // #[ cfg( feature = "either" ) ] // pub use ::either::*; - #[ cfg( feature = "type_constructor" ) ] + // #[ cfg( feature = "type_constructor" ) ] + // #[ doc( inline ) ] + // #[ allow( unused_imports ) ] + // pub use ::type_constructor::prelude::*; + + #[ cfg( feature = "dt_interval" ) ] #[ doc( inline ) ] #[ allow( unused_imports ) ] - pub use ::type_constructor::prelude::*; - #[ cfg( feature = "interval" ) ] + pub use crate::dependency::interval_adapter::prelude::*; + + #[ cfg( feature = "dt_collection" ) ] #[ doc( inline ) ] #[ allow( unused_imports ) ] - pub use ::interval_adapter::prelude::*; + pub use crate::dependency::collection_tools::prelude::*; + } diff --git a/module/core/data_type/src/lib.rs b/module/core/data_type/src/lib.rs index db11cf5e66..2ee15e40a1 100644 --- a/module/core/data_type/src/lib.rs +++ b/module/core/data_type/src/lib.rs @@ -7,7 +7,7 @@ // zzz : proc macro for standard lib epilogue // zzz : expose one_cell -/// Collection of primal data types. +/// Wrap dependencies under a namespace. pub mod dt; /// Namespace with dependencies. @@ -17,10 +17,12 @@ pub mod dependency { #[ cfg( feature = "either" ) ] pub use ::either; - #[ cfg( feature = "type_constructor" ) ] - pub use ::type_constructor; - #[ cfg( feature = "interval" ) ] + // #[ cfg( feature = "type_constructor" ) ] + // pub use ::type_constructor; // xxx : rid off + #[ cfg( feature = "dt_interval" ) ] pub use ::interval_adapter; + #[ cfg( feature = "dt_collection" ) ] + pub use ::collection_tools; } #[ doc( inline ) ] @@ -49,50 +51,74 @@ pub mod orphan /// Exposed namespace of the module. pub mod exposed { + #[ doc( inline ) ] #[ allow( unused_imports ) ] pub use super::prelude::*; + #[ doc( inline ) ] #[ allow( unused_imports ) ] pub use super::dt::exposed::*; + + #[ cfg( feature = "dt_interval" ) ] + #[ doc( inline ) ] + #[ allow( unused_imports ) ] + pub use crate::dependency::interval_adapter::exposed::*; + + #[ cfg( feature = "dt_collection" ) ] + #[ doc( inline ) ] + #[ allow( unused_imports ) ] + pub use crate::dependency::collection_tools::exposed::*; + } /// Prelude to use essentials: `use my_module::prelude::*`. pub mod prelude { + #[ doc( inline ) ] #[ allow( unused_imports ) ] pub use super::dt::prelude::*; - #[ cfg( not( feature = "no_std" ) ) ] - #[ cfg( feature = "prelude" ) ] + // #[ cfg( not( feature = "no_std" ) ) ] + // #[ cfg( feature = "prelude" ) ] + // #[ doc( inline ) ] + // #[ allow( unused_imports ) ] + // pub use std::collections:: + // { + // HashMap as Map, + // HashSet as Set, + // HashMap, + // HashSet, + // VecDeque, + // BTreeMap, + // BTreeSet, + // BinaryHeap, + // LinkedList, + // }; + + // #[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] + // #[ cfg( feature = "prelude" ) ] + // #[ doc( inline ) ] + // #[ allow( unused_imports ) ] + // pub use std::vec:: + // { + // Vec, + // Vec as DynArray, + // }; + + #[ cfg( feature = "dt_interval" ) ] #[ doc( inline ) ] #[ allow( unused_imports ) ] - pub use std::collections:: - { - HashMap as Map, - HashSet as Set, - HashMap, - HashSet, - VecDeque, - BTreeMap, - BTreeSet, - BinaryHeap, - LinkedList, - }; + pub use crate::dependency::interval_adapter::prelude::*; - #[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] - #[ cfg( feature = "prelude" ) ] + #[ cfg( feature = "dt_collection" ) ] #[ doc( inline ) ] #[ allow( unused_imports ) ] - pub use std::vec:: - { - Vec, - Vec as DynArray, - }; + pub use crate::dependency::collection_tools::prelude::*; // #[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] - #[ cfg( feature = "prelude" ) ] + #[ cfg( feature = "dt_prelude" ) ] #[ doc( inline ) ] #[ allow( unused_imports ) ] pub use core:: diff --git a/module/core/data_type/tests/inc/mod.rs b/module/core/data_type/tests/inc/mod.rs index 77e3836c0c..ae7aa08f8a 100644 --- a/module/core/data_type/tests/inc/mod.rs +++ b/module/core/data_type/tests/inc/mod.rs @@ -9,7 +9,6 @@ mod either_test; // #[ path = "../../../../core/type_constructor/tests/inc/mod.rs" ] // mod type_constructor; -// xxx2 : fix -// #[ cfg( any( feature = "interval", feature = "dt_interval" ) ) ] -// #[ path = "../../../../core/interval_adapter/tests/inc/mod.rs" ] -// mod interval_test; +#[ cfg( any( feature = "interval", feature = "dt_interval" ) ) ] +#[ path = "../../../../core/interval_adapter/tests/inc/mod.rs" ] +mod interval_test; diff --git a/module/core/data_type/tests/data_type_tests.rs b/module/core/data_type/tests/tests.rs similarity index 91% rename from module/core/data_type/tests/data_type_tests.rs rename to module/core/data_type/tests/tests.rs index 696303311a..26896b6193 100644 --- a/module/core/data_type/tests/data_type_tests.rs +++ b/module/core/data_type/tests/tests.rs @@ -6,8 +6,8 @@ #[ allow( unused_imports ) ] use data_type as the_module; + #[ allow( unused_imports ) ] use test_tools::exposed::*; -// #[ path = "./inc.rs" ] mod inc; diff --git a/module/core/derive_tools/Cargo.toml b/module/core/derive_tools/Cargo.toml index a873222be0..5709a8e50f 100644 --- a/module/core/derive_tools/Cargo.toml +++ b/module/core/derive_tools/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "derive_tools" -version = "0.21.0" +version = "0.23.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", @@ -67,6 +67,7 @@ default = [ "derive_from", "derive_inner_from", + "derive_new", # "use_std", ] @@ -102,7 +103,7 @@ full = [ "derive_display", "derive_from_str", - + "derive_clone_dyn", # "derive_clone_dyn_use_std", @@ -111,16 +112,17 @@ full = [ "derive_from", "derive_inner_from", + "derive_new", # "use_std", ] no_std = [] -use_alloc = [ "no_std" ] -enabled = [] +use_alloc = [ "no_std", "clone_dyn/use_alloc" ] +enabled = [ "derive_tools_meta/enabled" ] # nightly = [ "derive_more/nightly" ] -type_variadic_from = [ "variadic_from/type_variadic_from" ] +type_variadic_from = [ "variadic_from/enabled", "variadic_from/type_variadic_from" ] derive_variadic_from = [ "type_variadic_from", "derive_tools_meta/derive_variadic_from", "variadic_from/derive_variadic_from" ] # enable_track_caller = [ "derive_more", "derive_more/track-caller" ] @@ -163,13 +165,15 @@ derive_strum = [ "strum/std", "strum/derive", "strum/strum_macros" ] strum_phf = [ "strum/std", "strum/phf", "strum/strum_macros" ] # zzz : review features -derive_clone_dyn = [ "clone_dyn" ] +derive_clone_dyn = [ "clone_dyn/enabled" ] # derive_clone_dyn_use_std = [ "derive_clone_dyn", "clone_dyn/use_std" ] # derive_clone_dyn_no_std = [ "derive_clone_dyn", "clone_dyn/no_std" ] -derive_clone_dyn_use_alloc = [ "derive_clone_dyn", "clone_dyn/use_alloc" ] +# derive_clone_dyn_use_alloc = [ "derive_clone_dyn", "clone_dyn/use_alloc" ] derive_from = [ "derive_tools_meta/derive_from" ] derive_inner_from = [ "derive_tools_meta/derive_inner_from" ] +derive_new = [ "derive_tools_meta/derive_new" ] + parse_display = [ "parse-display" ] [dependencies] @@ -180,11 +184,11 @@ strum = { version = "~0.25", optional = true, default-features = false } # strum_macros = { version = "~0.25.3", optional = true, default-features = false } parse-display = { version = "~0.8.2", optional = true, default-features = false } + ## internal -derive_tools_meta = { workspace = true, features = [ "enabled" ] } +derive_tools_meta = { workspace = true, features = [] } variadic_from = { workspace = true, features = [] } -clone_dyn = { workspace = true, optional = true } -# xxx +clone_dyn = { workspace = true, features = [] } [dev-dependencies] test_tools = { workspace = true } diff --git a/module/core/derive_tools/Readme.md b/module/core/derive_tools/Readme.md index 99e0ba851c..746b6e4ec7 100644 --- a/module/core/derive_tools/Readme.md +++ b/module/core/derive_tools/Readme.md @@ -2,7 +2,7 @@ - [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/module_derive_tools_push.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/module_derive_tools_push.yml) [![docs.rs](https://img.shields.io/docsrs/derive_tools?color=e3e8f0&logo=docs.rs)](https://docs.rs/derive_tools) [![Open in Gitpod](https://raster.shields.io/static/v1?label=try&message=online&color=eee&logo=gitpod&logoColor=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module/core/derive_tools/examples/derive_tools_trivial.rs,RUN_POSTFIX=--example%20/home/sakapoi/Документи/wTools_fork/module/core/derive_tools/examples/derive_tools_trivial/https://github.com/Wandalen/wTools) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) + [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/module_derive_tools_push.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/module_derive_tools_push.yml) [![docs.rs](https://img.shields.io/docsrs/derive_tools?color=e3e8f0&logo=docs.rs)](https://docs.rs/derive_tools) [![Open in Gitpod](https://raster.shields.io/static/v1?label=try&message=online&color=eee&logo=gitpod&logoColor=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fcore%2Fderive_tools%2Fexamples%2Fderive_tools_trivial.rs,RUN_POSTFIX=--example%20derive_tools_trivial/https://github.com/Wandalen/wTools) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) ### Basic use-case diff --git a/module/core/derive_tools/examples/derive_tools_trivial.rs b/module/core/derive_tools/examples/derive_tools_trivial.rs index ff402f3c86..684f554329 100644 --- a/module/core/derive_tools/examples/derive_tools_trivial.rs +++ b/module/core/derive_tools/examples/derive_tools_trivial.rs @@ -1,4 +1,4 @@ -//! qqq : write proper description +//! for Petro : write proper description fn main() { diff --git a/module/core/derive_tools/src/lib.rs b/module/core/derive_tools/src/lib.rs index 3e29da3f59..d99c9c9455 100644 --- a/module/core/derive_tools/src/lib.rs +++ b/module/core/derive_tools/src/lib.rs @@ -2,15 +2,34 @@ #![ 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/derive_tools/latest/derive_tools/" ) ] - -//! -//! Collection of derives which extend STD. -//! - #![ doc = include_str!( concat!( env!( "CARGO_MANIFEST_DIR" ), "/", "Readme.md" ) ) ] -#[ cfg( feature = "enabled" ) ] -pub mod wtools; +// // xxx : implement derive new +// +// #[ 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() } +// } +// } + +// #[ cfg( feature = "enabled" ) ] +// pub mod wtools; #[ cfg( all( feature = "derive_more" ) ) ] #[ allow( unused_imports ) ] @@ -49,14 +68,18 @@ mod derive_more #[ cfg( feature = "derive_unwrap" ) ] pub use ::derive_more::Unwrap; - // qqq2 : list all - // qqq2 : make sure all features of derive_more is reexported + // qqq : list all + // qqq : make sure all features of derive_more is reexported } +#[ doc( inline ) ] +#[ cfg( any( feature = "derive_variadic_from", feature = "type_variadic_from" ) ) ] +pub use variadic_from as variadic; + // #[ cfg( feature = "derive_reflect" ) ] // pub mod reflect; -// use derive_tools_meta::Deref; +// use derive_tools_meta::Deref; // use derive_tools_meta::VariadicFrom; /// Namespace with dependencies. @@ -64,22 +87,31 @@ mod derive_more #[ cfg( feature = "enabled" ) ] pub mod dependency { + #[ doc( inline ) ] #[ cfg( feature = "derive_more" ) ] pub use ::derive_more; + #[ doc( inline ) ] #[ cfg( feature = "derive_strum" ) ] pub use ::strum; + #[ doc( inline ) ] #[ cfg( feature = "parse_display" ) ] pub use ::parse_display; + #[ doc( inline ) ] #[ cfg( feature = "clone_dyn" ) ] pub use ::clone_dyn; + #[ doc( inline ) ] #[ cfg( feature = "clone_dyn" ) ] pub use ::clone_dyn::dependency::*; + #[ doc( inline ) ] #[ cfg( any_derive ) ] pub use ::derive_tools_meta; + #[ doc( inline ) ] + #[ cfg( any( feature = "derive_variadic_from", feature = "type_variadic_from" ) ) ] + pub use ::variadic_from; } -#[ cfg( feature = "enabled" ) ] #[ doc( inline ) ] +#[ cfg( feature = "enabled" ) ] #[ allow( unused_imports ) ] pub use protected::*; @@ -94,9 +126,9 @@ pub mod protected #[ doc( inline ) ] #[ allow( unused_imports ) ] pub use ::clone_dyn::orphan::*; - #[ doc( inline ) ] - #[ allow( unused_imports ) ] - pub use super::wtools::orphan::*; + // #[ doc( inline ) ] + // #[ allow( unused_imports ) ] + // pub use super::wtools::orphan::*; // #[ cfg( feature = "derive_reflect" ) ] // #[ doc( inline ) ] // #[ allow( unused_imports ) ] @@ -130,6 +162,10 @@ pub mod exposed #[ allow( unused_imports ) ] pub use ::strum::*; + #[ cfg( any( feature = "derive_variadic_from", feature = "type_variadic_from" ) ) ] + #[ doc( inline ) ] + pub use ::variadic_from::exposed::*; + // #[ cfg( all( feature = "derive_more", feature = "derive_add" ) ) ] // #[ doc( inline ) ] // #[ allow( unused_imports ) ] @@ -213,9 +249,9 @@ pub mod exposed #[ allow( unused_imports ) ] pub use ::clone_dyn::exposed::*; - #[ doc( inline ) ] - #[ allow( unused_imports ) ] - pub use super::wtools::exposed::*; + // #[ doc( inline ) ] + // #[ allow( unused_imports ) ] + // pub use super::wtools::exposed::*; // #[ cfg( feature = "derive_reflect" ) ] // #[ doc( inline ) ] @@ -247,23 +283,31 @@ pub mod prelude #[ doc( inline ) ] #[ allow( unused_imports ) ] pub use ::clone_dyn; + #[ cfg( feature = "derive_clone_dyn" ) ] #[ doc( inline ) ] #[ allow( unused_imports ) ] pub use ::clone_dyn::prelude::*; - #[ cfg( feature = "derive_clone_dyn" ) ] + + // it should already be in predlue of clone_dyn + // #[ cfg( feature = "derive_clone_dyn" ) ] + // #[ doc( inline ) ] + // #[ allow( unused_imports ) ] + // pub use ::clone_dyn::clone_dyn; + + #[ cfg( any( feature = "derive_variadic_from", feature = "type_variadic_from" ) ) ] #[ doc( inline ) ] - #[ allow( unused_imports ) ] - pub use ::clone_dyn::clone_dyn; + pub use ::variadic_from::prelude::*; + // #[ cfg( feature = "derive_reflect" ) ] // #[ doc( inline ) ] // #[ allow( unused_imports ) ] // pub use super::reflect::prelude::*; - #[ doc( inline ) ] - #[ allow( unused_imports ) ] - pub use super::wtools::prelude::*; - #[ doc( no_inline ) ] - pub use super::wtools; + // #[ doc( inline ) ] + // #[ allow( unused_imports ) ] + // pub use super::wtools::prelude::*; + // #[ doc( no_inline ) ] + // pub use super::wtools; } diff --git a/module/core/derive_tools/src/wtools.rs b/module/core/derive_tools/src/wtools.rs deleted file mode 100644 index 97e8a54e95..0000000000 --- a/module/core/derive_tools/src/wtools.rs +++ /dev/null @@ -1,59 +0,0 @@ -//! -//! Types, which are extension of std. -//! - -// qqq : xxx : rid off the file - -/// Internal namespace. -pub( crate ) mod private -{ -} - -#[ cfg( feature = "type_variadic_from" ) ] -pub use ::variadic_from::wtools::from; -// pub mod from; - -/// Protected namespace of the module. -pub mod protected -{ - #[ doc( inline ) ] - #[ allow( unused_imports ) ] - pub use super::orphan::*; -} - -#[ doc( inline ) ] -#[ allow( unused_imports ) ] -pub use protected::*; - -/// 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 -{ - #[ doc( inline ) ] - #[ allow( unused_imports ) ] - pub use super::prelude::*; - #[ cfg( feature = "type_variadic_from" ) ] - #[ doc( inline ) ] - #[ allow( unused_imports ) ] - pub use super::from::orphan::*; -} - -#[ doc( inline ) ] -#[ allow( unused_imports ) ] -pub use exposed::*; - -/// Prelude to use essentials: `use my_module::prelude::*`. -pub mod prelude -{ - #[ cfg( feature = "type_variadic_from" ) ] - #[ doc( inline ) ] - #[ allow( unused_imports ) ] - pub use super::from::prelude::*; -} diff --git a/module/core/derive_tools/tests/inc/all_test.rs b/module/core/derive_tools/tests/inc/all_test.rs index a72ffa1741..c716416146 100644 --- a/module/core/derive_tools/tests/inc/all_test.rs +++ b/module/core/derive_tools/tests/inc/all_test.rs @@ -1,10 +1,10 @@ use super::*; -#[ derive( Debug, Clone, Copy, PartialEq, /* the_module::Default,*/ the_module::FromInner, the_module::InnerFrom, the_module::Deref, the_module::DerefMut, the_module::AsRef, the_module::AsMut ) ] +#[ derive( Debug, Clone, Copy, PartialEq, /* the_module::Default,*/ the_module::From, the_module::InnerFrom, the_module::Deref, the_module::DerefMut, the_module::AsRef, the_module::AsMut ) ] // #[ default( value = false ) ] pub struct IsTransparent( bool ); -// qqq2 : make Default derive working +// qqq : xxx : make Default derive working impl Default for IsTransparent { diff --git a/module/core/derive_tools/tests/inc/basic_test.rs b/module/core/derive_tools/tests/inc/basic_test.rs index f83df41abe..b02ae25e19 100644 --- a/module/core/derive_tools/tests/inc/basic_test.rs +++ b/module/core/derive_tools/tests/inc/basic_test.rs @@ -12,7 +12,6 @@ tests_impls! { use the_module::*; - // xxx : qqq : make it working #[ derive( From, InnerFrom, Display, FromStr, PartialEq, Debug ) ] #[ display( "{a}-{b}" ) ] struct Struct1 diff --git a/module/core/derive_tools/tests/inc/from_inner_manual_test.rs b/module/core/derive_tools/tests/inc/from/manual_test.rs similarity index 86% rename from module/core/derive_tools/tests/inc/from_inner_manual_test.rs rename to module/core/derive_tools/tests/inc/from/manual_test.rs index 2b48bca774..4add4ff66b 100644 --- a/module/core/derive_tools/tests/inc/from_inner_manual_test.rs +++ b/module/core/derive_tools/tests/inc/from/manual_test.rs @@ -15,4 +15,4 @@ impl From< bool > for IsTransparent } } -include!( "./only_test/from_inner.rs" ); +include!( "./only_test/basic.rs" ); diff --git a/module/core/derive_tools/tests/inc/from_inner_multiple_manual_test.rs b/module/core/derive_tools/tests/inc/from/multiple_manual_test.rs similarity index 83% rename from module/core/derive_tools/tests/inc/from_inner_multiple_manual_test.rs rename to module/core/derive_tools/tests/inc/from/multiple_manual_test.rs index 9d49fa7e36..b44fc6b651 100644 --- a/module/core/derive_tools/tests/inc/from_inner_multiple_manual_test.rs +++ b/module/core/derive_tools/tests/inc/from/multiple_manual_test.rs @@ -12,4 +12,4 @@ impl From< ( i32, bool ) > for StructWithManyFields } } -include!( "./only_test/from_inner_multiple.rs" ); +include!( "./only_test/multiple.rs" ); diff --git a/module/core/derive_tools/tests/inc/from_inner_multiple_named_manual_test.rs b/module/core/derive_tools/tests/inc/from/multiple_named_manual_test.rs similarity index 82% rename from module/core/derive_tools/tests/inc/from_inner_multiple_named_manual_test.rs rename to module/core/derive_tools/tests/inc/from/multiple_named_manual_test.rs index 148dff7a42..02b756b5f5 100644 --- a/module/core/derive_tools/tests/inc/from_inner_multiple_named_manual_test.rs +++ b/module/core/derive_tools/tests/inc/from/multiple_named_manual_test.rs @@ -16,4 +16,4 @@ impl From< ( i32, bool ) > for StructNamedFields } } -include!( "./only_test/from_inner_multiple_named.rs" ); +include!( "./only_test/multiple_named.rs" ); diff --git a/module/core/derive_tools/tests/inc/from/multiple_named_test.rs b/module/core/derive_tools/tests/inc/from/multiple_named_test.rs new file mode 100644 index 0000000000..ddb088c624 --- /dev/null +++ b/module/core/derive_tools/tests/inc/from/multiple_named_test.rs @@ -0,0 +1,10 @@ +use super::*; + +#[ derive( Debug, PartialEq, Eq, the_module::From ) ] +struct StructNamedFields +{ + a: i32, + b: bool, +} + +include!( "./only_test/multiple_named.rs" ); diff --git a/module/core/derive_tools/tests/inc/from/multiple_test.rs b/module/core/derive_tools/tests/inc/from/multiple_test.rs new file mode 100644 index 0000000000..925cd66ca0 --- /dev/null +++ b/module/core/derive_tools/tests/inc/from/multiple_test.rs @@ -0,0 +1,6 @@ +use super::*; + +#[ derive( Debug, PartialEq, Eq, the_module::From ) ] +struct StructWithManyFields( i32, bool ); + +include!( "./only_test/multiple.rs" ); diff --git a/module/core/derive_tools/tests/inc/from_inner_named_manual_test.rs b/module/core/derive_tools/tests/inc/from/named_manual_test.rs similarity index 80% rename from module/core/derive_tools/tests/inc/from_inner_named_manual_test.rs rename to module/core/derive_tools/tests/inc/from/named_manual_test.rs index d80f626439..6118d32e69 100644 --- a/module/core/derive_tools/tests/inc/from_inner_named_manual_test.rs +++ b/module/core/derive_tools/tests/inc/from/named_manual_test.rs @@ -15,4 +15,4 @@ impl From< i32 > for MyStruct } } -include!( "./only_test/from_inner_named.rs" ); +include!( "./only_test/named.rs" ); diff --git a/module/core/derive_tools/tests/inc/from/named_test.rs b/module/core/derive_tools/tests/inc/from/named_test.rs new file mode 100644 index 0000000000..49a1e564d1 --- /dev/null +++ b/module/core/derive_tools/tests/inc/from/named_test.rs @@ -0,0 +1,9 @@ +use super::*; + +#[ derive( Debug, PartialEq, Eq, the_module::From ) ] +struct MyStruct +{ + a : i32, +} + +include!( "./only_test/named.rs" ); diff --git a/module/core/derive_tools/tests/inc/only_test/from_inner.rs b/module/core/derive_tools/tests/inc/from/only_test/basic.rs similarity index 97% rename from module/core/derive_tools/tests/inc/only_test/from_inner.rs rename to module/core/derive_tools/tests/inc/from/only_test/basic.rs index df3fca75ea..f7f591e3c0 100644 --- a/module/core/derive_tools/tests/inc/only_test/from_inner.rs +++ b/module/core/derive_tools/tests/inc/from/only_test/basic.rs @@ -1,6 +1,6 @@ #[ test ] -fn from_inner_test() +fn from_test() { // let got = IsTransparent::default(); diff --git a/module/core/derive_tools/tests/inc/only_test/from_inner_multiple.rs b/module/core/derive_tools/tests/inc/from/only_test/multiple.rs similarity index 87% rename from module/core/derive_tools/tests/inc/only_test/from_inner_multiple.rs rename to module/core/derive_tools/tests/inc/from/only_test/multiple.rs index 4ef1750651..0386486e7c 100644 --- a/module/core/derive_tools/tests/inc/only_test/from_inner_multiple.rs +++ b/module/core/derive_tools/tests/inc/from/only_test/multiple.rs @@ -1,5 +1,5 @@ #[ test ] -fn from_inner_named() +fn from_named() { let got : StructWithManyFields = StructWithManyFields::from((10, true)); let exp = StructWithManyFields( 10 , true ); diff --git a/module/core/derive_tools/tests/inc/only_test/from_inner_multiple_named.rs b/module/core/derive_tools/tests/inc/from/only_test/multiple_named.rs similarity index 87% rename from module/core/derive_tools/tests/inc/only_test/from_inner_multiple_named.rs rename to module/core/derive_tools/tests/inc/from/only_test/multiple_named.rs index 3ba0ced760..b88a4f3872 100644 --- a/module/core/derive_tools/tests/inc/only_test/from_inner_multiple_named.rs +++ b/module/core/derive_tools/tests/inc/from/only_test/multiple_named.rs @@ -1,5 +1,5 @@ #[ test ] -fn from_inner_named() +fn from_named() { let got : StructNamedFields = StructNamedFields::from((10, true)); let exp = StructNamedFields{ a : 10 , b : true }; diff --git a/module/core/derive_tools/tests/inc/only_test/from_inner_named.rs b/module/core/derive_tools/tests/inc/from/only_test/named.rs similarity index 83% rename from module/core/derive_tools/tests/inc/only_test/from_inner_named.rs rename to module/core/derive_tools/tests/inc/from/only_test/named.rs index ff62e904eb..d749625ce6 100644 --- a/module/core/derive_tools/tests/inc/only_test/from_inner_named.rs +++ b/module/core/derive_tools/tests/inc/from/only_test/named.rs @@ -1,5 +1,5 @@ #[ test ] -fn from_inner_named() +fn from_named() { let got : MyStruct = MyStruct::from( 13 ); let exp = MyStruct { a : 13 }; diff --git a/module/core/derive_tools/tests/inc/only_test/from_inner_unit.rs b/module/core/derive_tools/tests/inc/from/only_test/unit.rs similarity index 82% rename from module/core/derive_tools/tests/inc/only_test/from_inner_unit.rs rename to module/core/derive_tools/tests/inc/from/only_test/unit.rs index 1cc2c51750..7e5b22ad51 100644 --- a/module/core/derive_tools/tests/inc/only_test/from_inner_unit.rs +++ b/module/core/derive_tools/tests/inc/from/only_test/unit.rs @@ -1,5 +1,5 @@ #[ test ] -fn from_inner_named() +fn from_named() { let got : UnitStruct = UnitStruct::from( () ); let exp = UnitStruct; diff --git a/module/core/derive_tools/tests/inc/from/only_test/variants.rs b/module/core/derive_tools/tests/inc/from/only_test/variants.rs new file mode 100644 index 0000000000..7ec89b1315 --- /dev/null +++ b/module/core/derive_tools/tests/inc/from/only_test/variants.rs @@ -0,0 +1,20 @@ +#[ allow( unused_imports ) ] +use super::*; + +#[ test ] +fn variant_from() +{ + + let got : GetData = From::from( "abc".to_string() ); + let exp = GetData::FromString( "abc".to_string() ); + a_id!( got, exp ); + + let got : GetData = From::from( ( "a".to_string(), "b".to_string() ) ); + let exp = GetData::FromPair( "a".to_string(), "b".to_string() ); + a_id!( got, exp ); + + let got : GetData = From::from( &b"abc"[ .. ] ); + let exp = GetData::FromBin( b"abc" ); + a_id!( got, exp ); + +} diff --git a/module/core/derive_tools/tests/inc/from/only_test/variants_duplicates.rs b/module/core/derive_tools/tests/inc/from/only_test/variants_duplicates.rs new file mode 100644 index 0000000000..9b126ff42e --- /dev/null +++ b/module/core/derive_tools/tests/inc/from/only_test/variants_duplicates.rs @@ -0,0 +1,20 @@ +#[ allow( unused_imports ) ] +use super::*; + +#[ test ] +fn variant_from_duplicates() +{ + + let got : GetData = From::from( &b"abc"[ .. ] ); + let exp = GetData::FromBin( b"abc" ); + a_id!( got, exp ); + + let got : GetData = From::from( "abc".to_string() ); + let exp = GetData::FromString2( "abc".to_string() ); + a_id!( got, exp ); + + let got : GetData = From::from( ( "a".to_string(), "b".to_string() ) ); + let exp = GetData::FromPair2( "a".to_string(), "b".to_string() ); + a_id!( got, exp ); + +} diff --git a/module/core/derive_tools/tests/inc/from/only_test/variants_generics.rs b/module/core/derive_tools/tests/inc/from/only_test/variants_generics.rs new file mode 100644 index 0000000000..be2731277e --- /dev/null +++ b/module/core/derive_tools/tests/inc/from/only_test/variants_generics.rs @@ -0,0 +1,12 @@ +#[ allow( unused_imports ) ] +use super::*; + +#[ test ] +fn variant_from() +{ + + let got : GetData< '_, str > = From::from( "abc" ); + let exp = GetData::< '_, str >::FromT( "abc" ); + a_id!( got, exp ); + +} diff --git a/module/core/derive_tools/tests/inc/from_inner_test.rs b/module/core/derive_tools/tests/inc/from/test.rs similarity index 56% rename from module/core/derive_tools/tests/inc/from_inner_test.rs rename to module/core/derive_tools/tests/inc/from/test.rs index 4848773fde..1214ad5a43 100644 --- a/module/core/derive_tools/tests/inc/from_inner_test.rs +++ b/module/core/derive_tools/tests/inc/from/test.rs @@ -3,8 +3,8 @@ use super::*; // use diagnostics_tools::prelude::*; // use derives::*; -#[ derive( Debug, Clone, Copy, PartialEq, the_module::FromInner ) ] +#[ derive( Debug, Clone, Copy, PartialEq, the_module::From ) ] pub struct IsTransparent( bool ); // include!( "./manual/basic.rs" ); -include!( "./only_test/from_inner.rs" ); +include!( "./only_test/basic.rs" ); diff --git a/module/core/derive_tools/tests/inc/from_inner_unit_manual_test.rs b/module/core/derive_tools/tests/inc/from/unit_manual_test.rs similarity index 80% rename from module/core/derive_tools/tests/inc/from_inner_unit_manual_test.rs rename to module/core/derive_tools/tests/inc/from/unit_manual_test.rs index 78b7578956..dc767e9fbb 100644 --- a/module/core/derive_tools/tests/inc/from_inner_unit_manual_test.rs +++ b/module/core/derive_tools/tests/inc/from/unit_manual_test.rs @@ -12,4 +12,4 @@ impl From< () > for UnitStruct } } -include!( "./only_test/from_inner_unit.rs" ); +include!( "./only_test/unit.rs" ); diff --git a/module/core/derive_tools/tests/inc/from/unit_test.rs b/module/core/derive_tools/tests/inc/from/unit_test.rs new file mode 100644 index 0000000000..82690e5190 --- /dev/null +++ b/module/core/derive_tools/tests/inc/from/unit_test.rs @@ -0,0 +1,6 @@ +use super::*; + +#[ derive( Debug, Clone, Copy, PartialEq, the_module::From ) ] +struct UnitStruct; + +include!( "./only_test/unit.rs" ); diff --git a/module/core/derive_tools/tests/inc/from/variants_collisions.rs b/module/core/derive_tools/tests/inc/from/variants_collisions.rs new file mode 100644 index 0000000000..aefa3be96f --- /dev/null +++ b/module/core/derive_tools/tests/inc/from/variants_collisions.rs @@ -0,0 +1,27 @@ +#![ allow( non_snake_case ) ] +#![ allow( unused_imports ) ] +use super::*; + +pub mod core {} +pub mod std {} +pub mod marker {} + +pub mod FromString {} +pub mod FromPair {} +pub mod FromBin {} + +#[ derive( Debug, PartialEq, the_module::From ) ] +// #[ debug ] +pub enum GetData +{ + #[ allow( dead_code ) ] + Nothing, + FromString( String ), + FromPair( String, String ), + FromBin( &'static [ u8 ] ), +} + +// == begin of generated +// == end of generated + +include!( "./only_test/variants.rs" ); diff --git a/module/core/derive_tools/tests/inc/from/variants_derive.rs b/module/core/derive_tools/tests/inc/from/variants_derive.rs new file mode 100644 index 0000000000..27792afbdc --- /dev/null +++ b/module/core/derive_tools/tests/inc/from/variants_derive.rs @@ -0,0 +1,18 @@ +#[ allow( unused_imports ) ] +use super::*; + +#[ derive( Debug, PartialEq, the_module::From ) ] +// #[ debug ] +pub enum GetData +{ + #[ allow( dead_code ) ] + Nothing, + FromString( String ), + FromPair( String, String ), + FromBin( &'static [ u8 ] ), +} + +// == begin of generated +// == end of generated + +include!( "./only_test/variants.rs" ); diff --git a/module/core/derive_tools/tests/inc/from/variants_duplicates_all_off.rs b/module/core/derive_tools/tests/inc/from/variants_duplicates_all_off.rs new file mode 100644 index 0000000000..1eb00d2920 --- /dev/null +++ b/module/core/derive_tools/tests/inc/from/variants_duplicates_all_off.rs @@ -0,0 +1,45 @@ +#![ allow( dead_code ) ] +#[ allow( unused_imports ) ] +use super::*; + +#[ derive( Debug, PartialEq, the_module::From ) ] +// #[ debug ] +pub enum GetData +{ + Nothing, + Nothing2, + #[ from( off ) ] + FromString( String ), + #[ from( off ) ] + FromString2( String ), + #[ from( off ) ] + FromPair( String, String ), + #[ from( off ) ] + FromPair2( String, String ), + FromBin( &'static [ u8 ] ), + Nothing3, +} + +impl From< String > for GetData +{ + #[ inline ] + fn from( src : String ) -> Self + { + Self::FromString2( src ) + } +} + +impl From< ( String, String ) > for GetData +{ + #[ inline ] + fn from( src : ( String, String ) ) -> Self + { + Self::FromPair2( src.0, src.1 ) + } +} + +// == begin of generated + +// == end of generated + +include!( "./only_test/variants_duplicates.rs" ); diff --git a/module/core/derive_tools/tests/inc/from/variants_duplicates_some_off.rs b/module/core/derive_tools/tests/inc/from/variants_duplicates_some_off.rs new file mode 100644 index 0000000000..094d57a5f1 --- /dev/null +++ b/module/core/derive_tools/tests/inc/from/variants_duplicates_some_off.rs @@ -0,0 +1,25 @@ +#![ allow( dead_code ) ] +#[ allow( unused_imports ) ] +use super::*; + +#[ derive( Debug, PartialEq, the_module::From ) ] +// #[ debug ] +pub enum GetData +{ + Nothing, + Nothing2, + #[ from( off ) ] + FromString( String ), + FromString2( String ), + #[ from( off ) ] + FromPair( String, String ), + FromPair2( String, String ), + FromBin( &'static [ u8 ] ), + Nothing3, +} + +// == begin of generated + +// == end of generated + +include!( "./only_test/variants_duplicates.rs" ); diff --git a/module/core/derive_tools/tests/inc/from/variants_duplicates_some_off_default_off.rs b/module/core/derive_tools/tests/inc/from/variants_duplicates_some_off_default_off.rs new file mode 100644 index 0000000000..282b327e23 --- /dev/null +++ b/module/core/derive_tools/tests/inc/from/variants_duplicates_some_off_default_off.rs @@ -0,0 +1,27 @@ +#![ allow( dead_code ) ] +#[ allow( unused_imports ) ] +use super::*; + +#[ derive( Debug, PartialEq, the_module::From ) ] +#[ from( off ) ] +// #[ debug ] +pub enum GetData +{ + Nothing, + Nothing2, + FromString( String ), + #[ from( on ) ] + // #[ from( debug ) ] + FromString2( String ), + FromPair( String, String ), + #[ from( on ) ] + FromPair2( String, String ), + #[ from( on ) ] + FromBin( &'static [ u8 ] ), + Nothing3, +} + +// == begin of generated +// == end of generated + +include!( "./only_test/variants_duplicates.rs" ); diff --git a/module/core/derive_tools/tests/inc/from/variants_generics.rs b/module/core/derive_tools/tests/inc/from/variants_generics.rs new file mode 100644 index 0000000000..c163e39b7f --- /dev/null +++ b/module/core/derive_tools/tests/inc/from/variants_generics.rs @@ -0,0 +1,17 @@ +#![ allow( dead_code ) ] +#[ allow( unused_imports ) ] +use super::*; +use derive_tools::From; + +#[ derive( Debug, PartialEq, From ) ] +// #[ debug ] +pub enum GetData< 'a, T : ToString + ?Sized = str > +{ + Nothing, + FromT( &'a T ), +} + +// == begin of generated +// == end of generated + +include!( "./only_test/variants_generics.rs" ); diff --git a/module/core/derive_tools/tests/inc/from/variants_generics_where.rs b/module/core/derive_tools/tests/inc/from/variants_generics_where.rs new file mode 100644 index 0000000000..ec96c5313b --- /dev/null +++ b/module/core/derive_tools/tests/inc/from/variants_generics_where.rs @@ -0,0 +1,19 @@ +#![ allow( dead_code ) ] +#[ allow( unused_imports ) ] +use super::*; +use derive_tools::From; + +#[ derive( Debug, PartialEq, From ) ] +// #[ debug ] +pub enum GetData< 'a, T = str > +where + T : ToString + ?Sized, +{ + Nothing, + FromT( &'a T ), +} + +// == begin of generated +// == end of generated + +include!( "./only_test/variants_generics.rs" ); diff --git a/module/core/derive_tools/tests/inc/from/variants_manual.rs b/module/core/derive_tools/tests/inc/from/variants_manual.rs new file mode 100644 index 0000000000..9cd6e1e723 --- /dev/null +++ b/module/core/derive_tools/tests/inc/from/variants_manual.rs @@ -0,0 +1,41 @@ +#[ allow( unused_imports ) ] +use super::*; + +#[ derive( Debug, PartialEq ) ] +pub enum GetData +{ + #[ allow( dead_code ) ] + Nothing, + FromString( String ), + FromPair( String, String ), + FromBin( &'static [ u8 ] ), +} + +impl From< String > for GetData +{ + #[ inline ] + fn from( src : String ) -> Self + { + Self::FromString( src ) + } +} + +impl From< ( String, String ) > for GetData +{ + #[ inline ] + fn from( src : ( String, String ) ) -> Self + { + Self::FromPair( src.0, src.1 ) + } +} + +impl From< &'static [ u8 ] > for GetData +{ + #[ inline ] + fn from( src : &'static [ u8 ] ) -> Self + { + Self::FromBin( src ) + } +} + +include!( "./only_test/variants.rs" ); diff --git a/module/core/derive_tools/tests/inc/from_inner_multiple_named_test.rs b/module/core/derive_tools/tests/inc/from_inner_multiple_named_test.rs deleted file mode 100644 index 436683a3b5..0000000000 --- a/module/core/derive_tools/tests/inc/from_inner_multiple_named_test.rs +++ /dev/null @@ -1,10 +0,0 @@ -use super::*; - -#[ derive( Debug, PartialEq, Eq, the_module::FromInner ) ] -struct StructNamedFields -{ - a: i32, - b: bool, -} - -include!( "./only_test/from_inner_multiple_named.rs" ); diff --git a/module/core/derive_tools/tests/inc/from_inner_multiple_test.rs b/module/core/derive_tools/tests/inc/from_inner_multiple_test.rs deleted file mode 100644 index dd18c948c9..0000000000 --- a/module/core/derive_tools/tests/inc/from_inner_multiple_test.rs +++ /dev/null @@ -1,6 +0,0 @@ -use super::*; - -#[ derive( Debug, PartialEq, Eq, the_module::FromInner ) ] -struct StructWithManyFields( i32, bool ); - -include!( "./only_test/from_inner_multiple.rs" ); diff --git a/module/core/derive_tools/tests/inc/from_inner_named_test.rs b/module/core/derive_tools/tests/inc/from_inner_named_test.rs deleted file mode 100644 index 0ea85ef088..0000000000 --- a/module/core/derive_tools/tests/inc/from_inner_named_test.rs +++ /dev/null @@ -1,9 +0,0 @@ -use super::*; - -#[ derive( Debug, PartialEq, Eq, the_module::FromInner ) ] -struct MyStruct -{ - a: i32, -} - -include!( "./only_test/from_inner_named.rs" ); diff --git a/module/core/derive_tools/tests/inc/from_inner_unit_test.rs b/module/core/derive_tools/tests/inc/from_inner_unit_test.rs deleted file mode 100644 index 2aa637a05b..0000000000 --- a/module/core/derive_tools/tests/inc/from_inner_unit_test.rs +++ /dev/null @@ -1,6 +0,0 @@ -use super::*; - -#[ derive( Debug, Clone, Copy, PartialEq, the_module::FromInner ) ] -struct UnitStruct; - -include!( "./only_test/from_inner_unit.rs" ); diff --git a/module/core/derive_tools/tests/inc/inner_from_manual_test.rs b/module/core/derive_tools/tests/inc/inner_from/manual_test.rs similarity index 87% rename from module/core/derive_tools/tests/inc/inner_from_manual_test.rs rename to module/core/derive_tools/tests/inc/inner_from/manual_test.rs index 7e1d5a5ee6..4313f84564 100644 --- a/module/core/derive_tools/tests/inc/inner_from_manual_test.rs +++ b/module/core/derive_tools/tests/inc/inner_from/manual_test.rs @@ -15,4 +15,4 @@ impl From< IsTransparent > for bool } } -include!( "./only_test/inner_from.rs" ); +include!( "./only_test/basic.rs" ); diff --git a/module/core/derive_tools/tests/inc/inner_from_multiple_manual_test.rs b/module/core/derive_tools/tests/inc/inner_from/multiple_manual_test.rs similarity index 83% rename from module/core/derive_tools/tests/inc/inner_from_multiple_manual_test.rs rename to module/core/derive_tools/tests/inc/inner_from/multiple_manual_test.rs index 5b59a8a389..2bc7587221 100644 --- a/module/core/derive_tools/tests/inc/inner_from_multiple_manual_test.rs +++ b/module/core/derive_tools/tests/inc/inner_from/multiple_manual_test.rs @@ -12,4 +12,4 @@ impl From< StructWithManyFields > for ( i32, bool ) } } -include!( "./only_test/inner_from_multiple.rs" ); +include!( "./only_test/multiple.rs" ); diff --git a/module/core/derive_tools/tests/inc/inner_from_multiple_named_manual_test.rs b/module/core/derive_tools/tests/inc/inner_from/multiple_named_manual_test.rs similarity index 81% rename from module/core/derive_tools/tests/inc/inner_from_multiple_named_manual_test.rs rename to module/core/derive_tools/tests/inc/inner_from/multiple_named_manual_test.rs index 69db46283f..0a934e454f 100644 --- a/module/core/derive_tools/tests/inc/inner_from_multiple_named_manual_test.rs +++ b/module/core/derive_tools/tests/inc/inner_from/multiple_named_manual_test.rs @@ -16,4 +16,4 @@ impl From< StructNamedFields > for ( i32, bool ) } } -include!( "./only_test/inner_from_multiple_named.rs" ); +include!( "./only_test/multiple_named.rs" ); diff --git a/module/core/derive_tools/tests/inc/inner_from_multiple_named_test.rs b/module/core/derive_tools/tests/inc/inner_from/multiple_named_test.rs similarity index 69% rename from module/core/derive_tools/tests/inc/inner_from_multiple_named_test.rs rename to module/core/derive_tools/tests/inc/inner_from/multiple_named_test.rs index 07f93b2d15..6177841adf 100644 --- a/module/core/derive_tools/tests/inc/inner_from_multiple_named_test.rs +++ b/module/core/derive_tools/tests/inc/inner_from/multiple_named_test.rs @@ -7,4 +7,4 @@ struct StructNamedFields b: bool, } -include!( "./only_test/inner_from_multiple_named.rs" ); +include!( "./only_test/multiple_named.rs" ); diff --git a/module/core/derive_tools/tests/inc/inner_from_multiple_test.rs b/module/core/derive_tools/tests/inc/inner_from/multiple_test.rs similarity index 70% rename from module/core/derive_tools/tests/inc/inner_from_multiple_test.rs rename to module/core/derive_tools/tests/inc/inner_from/multiple_test.rs index 6c2fe1f1ef..c99e112ca4 100644 --- a/module/core/derive_tools/tests/inc/inner_from_multiple_test.rs +++ b/module/core/derive_tools/tests/inc/inner_from/multiple_test.rs @@ -3,4 +3,4 @@ use super::*; #[ derive( Debug, PartialEq, Eq, the_module::InnerFrom ) ] struct StructWithManyFields( i32, bool ); -include!( "./only_test/inner_from_multiple.rs" ); +include!( "./only_test/multiple.rs" ); diff --git a/module/core/derive_tools/tests/inc/inner_from_named_manual_test.rs b/module/core/derive_tools/tests/inc/inner_from/named_manual_test.rs similarity index 80% rename from module/core/derive_tools/tests/inc/inner_from_named_manual_test.rs rename to module/core/derive_tools/tests/inc/inner_from/named_manual_test.rs index d79107577a..4d7805644d 100644 --- a/module/core/derive_tools/tests/inc/inner_from_named_manual_test.rs +++ b/module/core/derive_tools/tests/inc/inner_from/named_manual_test.rs @@ -15,4 +15,4 @@ impl From< MyStruct > for i32 } } -include!( "./only_test/inner_from_named.rs" ); +include!( "./only_test/named.rs" ); diff --git a/module/core/derive_tools/tests/inc/inner_from_named_test.rs b/module/core/derive_tools/tests/inc/inner_from/named_test.rs similarity index 69% rename from module/core/derive_tools/tests/inc/inner_from_named_test.rs rename to module/core/derive_tools/tests/inc/inner_from/named_test.rs index da449524f3..2f6d3ccccc 100644 --- a/module/core/derive_tools/tests/inc/inner_from_named_test.rs +++ b/module/core/derive_tools/tests/inc/inner_from/named_test.rs @@ -6,4 +6,4 @@ struct MyStruct a: i32, } -include!( "./only_test/inner_from_named.rs" ); +include!( "./only_test/named.rs" ); diff --git a/module/core/derive_tools/tests/inc/only_test/inner_from.rs b/module/core/derive_tools/tests/inc/inner_from/only_test/basic.rs similarity index 100% rename from module/core/derive_tools/tests/inc/only_test/inner_from.rs rename to module/core/derive_tools/tests/inc/inner_from/only_test/basic.rs diff --git a/module/core/derive_tools/tests/inc/only_test/inner_from_multiple.rs b/module/core/derive_tools/tests/inc/inner_from/only_test/multiple.rs similarity index 85% rename from module/core/derive_tools/tests/inc/only_test/inner_from_multiple.rs rename to module/core/derive_tools/tests/inc/inner_from/only_test/multiple.rs index dca4fc2884..776347fd66 100644 --- a/module/core/derive_tools/tests/inc/only_test/inner_from_multiple.rs +++ b/module/core/derive_tools/tests/inc/inner_from/only_test/multiple.rs @@ -1,5 +1,5 @@ #[ test ] -fn from_inner_named() +fn from_named() { let got : ( i32, bool ) = StructWithManyFields( 10, true ).into(); let exp = ( 10 , true ); diff --git a/module/core/derive_tools/tests/inc/only_test/inner_from_multiple_named.rs b/module/core/derive_tools/tests/inc/inner_from/only_test/multiple_named.rs similarity index 85% rename from module/core/derive_tools/tests/inc/only_test/inner_from_multiple_named.rs rename to module/core/derive_tools/tests/inc/inner_from/only_test/multiple_named.rs index 0ac967c2f7..05b9a66e9d 100644 --- a/module/core/derive_tools/tests/inc/only_test/inner_from_multiple_named.rs +++ b/module/core/derive_tools/tests/inc/inner_from/only_test/multiple_named.rs @@ -1,5 +1,5 @@ #[ test ] -fn from_inner_named() +fn from_named() { let got : ( i32, bool ) = StructNamedFields{ a: 10, b: true }.into(); let exp = ( 10 , true ); diff --git a/module/core/derive_tools/tests/inc/only_test/inner_from_named.rs b/module/core/derive_tools/tests/inc/inner_from/only_test/named.rs similarity index 100% rename from module/core/derive_tools/tests/inc/only_test/inner_from_named.rs rename to module/core/derive_tools/tests/inc/inner_from/only_test/named.rs diff --git a/module/core/derive_tools/tests/inc/only_test/inner_from_unit.rs b/module/core/derive_tools/tests/inc/inner_from/only_test/unit.rs similarity index 100% rename from module/core/derive_tools/tests/inc/only_test/inner_from_unit.rs rename to module/core/derive_tools/tests/inc/inner_from/only_test/unit.rs diff --git a/module/core/derive_tools/tests/inc/inner_from_test.rs b/module/core/derive_tools/tests/inc/inner_from/test.rs similarity index 69% rename from module/core/derive_tools/tests/inc/inner_from_test.rs rename to module/core/derive_tools/tests/inc/inner_from/test.rs index b2c70b3eed..25ff2921e0 100644 --- a/module/core/derive_tools/tests/inc/inner_from_test.rs +++ b/module/core/derive_tools/tests/inc/inner_from/test.rs @@ -6,5 +6,4 @@ use super::*; #[ derive( Debug, Clone, Copy, PartialEq, the_module::InnerFrom ) ] pub struct IsTransparent( bool ); -// include!( "./manual/basic.rs" ); -include!( "./only_test/inner_from.rs" ); +include!( "./only_test/basic.rs" ); diff --git a/module/core/derive_tools/tests/inc/inner_from_unit_manual_test.rs b/module/core/derive_tools/tests/inc/inner_from/unit_manual_test.rs similarity index 83% rename from module/core/derive_tools/tests/inc/inner_from_unit_manual_test.rs rename to module/core/derive_tools/tests/inc/inner_from/unit_manual_test.rs index a4da6ca8f7..351db13dbb 100644 --- a/module/core/derive_tools/tests/inc/inner_from_unit_manual_test.rs +++ b/module/core/derive_tools/tests/inc/inner_from/unit_manual_test.rs @@ -13,4 +13,4 @@ impl From< UnitStruct > for () } // include!( "./manual/basic.rs" ); -include!( "./only_test/inner_from_unit.rs" ); +include!( "./only_test/unit.rs" ); diff --git a/module/core/derive_tools/tests/inc/inner_from_unit_test.rs b/module/core/derive_tools/tests/inc/inner_from/unit_test.rs similarity index 70% rename from module/core/derive_tools/tests/inc/inner_from_unit_test.rs rename to module/core/derive_tools/tests/inc/inner_from/unit_test.rs index 0bc0f38fe5..6d60f9cc6a 100644 --- a/module/core/derive_tools/tests/inc/inner_from_unit_test.rs +++ b/module/core/derive_tools/tests/inc/inner_from/unit_test.rs @@ -4,4 +4,4 @@ use super::*; pub struct UnitStruct; -include!( "./only_test/inner_from_unit.rs" ); +include!( "./only_test/unit.rs" ); diff --git a/module/core/derive_tools/tests/inc/mod.rs b/module/core/derive_tools/tests/inc/mod.rs index babb3aca97..f100189dbf 100644 --- a/module/core/derive_tools/tests/inc/mod.rs +++ b/module/core/derive_tools/tests/inc/mod.rs @@ -1,9 +1,18 @@ use super::*; -mod basic_test; +// = import tests of clone_dyn + +#[ cfg( feature = "derive_clone_dyn" ) ] +#[ path = "../../../../core/clone_dyn/tests/inc/mod.rs" ] +mod clone_dyn_test; + +// = import tests of variadic_from -// #[ cfg( any( feature = "derive_clone_dyn_use_std", feature = "derive_clone_dyn_use_alloc" ) ) ] -// mod clone_dyn_test; +#[ cfg( any( feature = "derive_variadic_from", feature = "type_variadic_from" ) ) ] +#[ path = "../../../../../module/core/variadic_from/tests/inc/mod.rs" ] +mod variadic_from_test; + +// = own tests mod all_manual_test; #[ cfg @@ -20,6 +29,8 @@ mod all_manual_test; )] mod all_test; +mod basic_test; + mod as_mut_manual_test; #[ cfg( feature = "derive_as_mut" ) ] mod as_mut_test; @@ -36,51 +47,53 @@ mod deref_mut_manual_test; #[ cfg( feature = "derive_deref_mut" ) ] mod deref_mut_test; -mod from_inner_manual_test; -mod from_inner_named_manual_test; -mod from_inner_multiple_named_manual_test; -mod from_inner_multiple_manual_test; -mod from_inner_unit_manual_test; #[ cfg( feature = "derive_from" ) ] -mod from_inner_test; -#[ cfg( feature = "derive_from" ) ] -mod from_inner_named_test; -#[ cfg( feature = "derive_from" ) ] -mod from_inner_multiple_named_test; -#[ cfg( feature = "derive_from" ) ] -mod from_inner_unit_test; -#[ cfg( feature = "derive_from" ) ] -mod from_inner_multiple_test; +#[ path = "from" ] +mod tests +{ + #[ allow( unused_imports ) ] + use super::*; + + mod named_test; + mod named_manual_test; + + mod manual_test; + mod multiple_named_manual_test; + mod multiple_manual_test; + mod unit_manual_test; + mod test; + mod multiple_named_test; + mod unit_test; + mod multiple_test; + + mod variants_manual; + mod variants_derive; + + mod variants_duplicates_all_off; + mod variants_duplicates_some_off; + mod variants_duplicates_some_off_default_off; + + mod variants_generics; + mod variants_generics_where; + mod variants_collisions; +} -mod inner_from_manual_test; -mod inner_from_named_manual_test; -mod inner_from_multiple_named_manual_test; -mod inner_from_multiple_manual_test; -mod inner_from_unit_manual_test; -#[ cfg( feature = "derive_inner_from" ) ] -mod inner_from_test; -#[ cfg( feature = "derive_inner_from" ) ] -mod inner_from_named_test; -#[ cfg( feature = "derive_inner_from" ) ] -mod inner_from_multiple_named_test; -#[ cfg( feature = "derive_inner_from" ) ] -mod inner_from_unit_test; #[ cfg( feature = "derive_inner_from" ) ] -mod inner_from_multiple_test; - -// qqq : xxx : fix -// #[ cfg( all( feature = "type_variadic_from" ) ) ] -// mod variadic_from_manual_test; -// -// #[ cfg( all( feature = "type_variadic_from" ) ) ] -// mod variadic_from_manual_beyond_test; -// -// // #[ cfg( all( feature = "derive_variadic_from", feature = "type_variadic_from" ) ) ] -// // mod variadic_from_derive_test; -// // xxx : fix -// #[ cfg( all( feature = "derive_variadic_from", feature = "type_variadic_from" ) ) ] -// mod variadic_from2_derive; - -// #[ cfg( any( feature = "derive_variadic_from", feature = "type_variadic_from" ) ) ] -// #[ path = "../../../../../module/core/variadic_from/tests/inc/mod.rs" ] -// mod variadic_tests; +#[ path = "inner_from" ] +mod inner_from_tests +{ + #[ allow( unused_imports ) ] + use super::*; + + mod manual_test; + mod named_manual_test; + mod multiple_named_manual_test; + mod multiple_manual_test; + mod unit_manual_test; + mod test; + mod named_test; + mod multiple_named_test; + mod unit_test; + mod multiple_test; + +} diff --git a/module/core/derive_tools/tests/inc/only_test/all.rs b/module/core/derive_tools/tests/inc/only_test/all.rs index 9708a9f8cf..5fe5831993 100644 --- a/module/core/derive_tools/tests/inc/only_test/all.rs +++ b/module/core/derive_tools/tests/inc/only_test/all.rs @@ -7,7 +7,7 @@ fn basic_test() let exp = IsTransparent( true ); a_id!( got, exp ); - // FromInner + // From let got = IsTransparent::from( true ); let exp = IsTransparent( true ); diff --git a/module/core/derive_tools/tests/inc/only_test/reflect_struct.rs b/module/core/derive_tools/tests/inc/only_test/reflect_struct.rs deleted file mode 100644 index a376802fc5..0000000000 --- a/module/core/derive_tools/tests/inc/only_test/reflect_struct.rs +++ /dev/null @@ -1,28 +0,0 @@ -#[ test ] -fn reflect_basic_test() -{ - use reflect::Entity; - - let ins = Struct1 - { - f1 : 1, - f2 : "2".into(), - f3 : "3", - }; - - a_id!( reflect::reflect( &ins ).is_container(), true ); - a_id!( reflect::reflect( &ins ).len(), 3 ); - a_id!( reflect::reflect( &ins ).type_name(), "derive_tests::inc::reflect_struct_manual_test::Struct1" ); - let names = reflect::reflect( &ins ).elements().map( | e | e.key ).collect::< Vec< _ > >(); - a_id!( names, vec![ reflect::Primitive::str( "f1" ), reflect::Primitive::str( "f2" ), reflect::Primitive::str( "f3" ) ] ); - let types = reflect::reflect( &ins ).elements().map( | e | e.val.type_name() ).collect::< Vec< _ > >(); - a_id!( types, vec![ "i32", "alloc::string::String", "&str" ] ); - - let f1 = reflect::reflect( &ins ).elements().next().unwrap(); - a_id!( f1.key, reflect::Primitive::str( "f1" ) ); - a_id!( f1.val.is_container(), false ); - a_id!( f1.val.len(), 0 ); - a_id!( f1.val.type_name(), "i32" ); - a_id!( f1.val.elements().collect::< Vec< _ > >(), vec![] ); - -} diff --git a/module/core/derive_tools/tests/inc/only_test/reflect_struct_in_struct.rs b/module/core/derive_tools/tests/inc/only_test/reflect_struct_in_struct.rs deleted file mode 100644 index 9d205fc776..0000000000 --- a/module/core/derive_tools/tests/inc/only_test/reflect_struct_in_struct.rs +++ /dev/null @@ -1,31 +0,0 @@ -#[ test ] -fn reflect_struct_in_struct() -{ - use reflect::Entity; - - let ins = Struct1 - { - f1 : 1, - f2 : "2".into(), - f3 : Struct2 { s1 : 10, s2 : "20".into(), s3 : "30" }, - }; - - a_id!( reflect::reflect( &ins ).is_container(), true ); - a_id!( reflect::reflect( &ins ).len(), 3 ); - a_id!( reflect::reflect( &ins ).type_name(), "derive_tests::inc::reflect_struct_in_struct_manual_test::Struct1" ); - let names = reflect::reflect( &ins ).elements().map( | e | e.key ).collect::< Vec< _ > >(); - a_id!( names, vec![ reflect::Primitive::str( "f1" ), reflect::Primitive::str( "f2" ), reflect::Primitive::str( "f3" ) ] ); - let types = reflect::reflect( &ins ).elements().map( | e | e.val.type_name() ).collect::< Vec< _ > >(); - a_id!( types, vec![ "i32", "alloc::string::String", "derive_tests::inc::reflect_struct_in_struct_manual_test::Struct2" ] ); - - let f3 = reflect::reflect( &ins ).elements().skip( 2 ).next().unwrap(); - a_id!( f3.key, reflect::Primitive::str( "f3" ) ); - a_id!( f3.val.is_container(), true ); - a_id!( f3.val.len(), 3 ); - a_id!( f3.val.type_name(), "derive_tests::inc::reflect_struct_in_struct_manual_test::Struct2" ); - let names = f3.val.elements().map( | e | e.key ).collect::< Vec< _ > >(); - a_id!( names, vec![ reflect::Primitive::str( "s1" ), reflect::Primitive::str( "s2" ), reflect::Primitive::str( "s3" ) ] ); - let types = f3.val.elements().map( | e | e.val.type_name() ).collect::< Vec< _ > >(); - a_id!( types, vec![ "i32", "alloc::string::String", "&str" ] ); - -} diff --git a/module/core/derive_tools/tests/inc/only_test/reflect_struct_with_lifetime.rs b/module/core/derive_tools/tests/inc/only_test/reflect_struct_with_lifetime.rs deleted file mode 100644 index 35adf13d24..0000000000 --- a/module/core/derive_tools/tests/inc/only_test/reflect_struct_with_lifetime.rs +++ /dev/null @@ -1,49 +0,0 @@ -#[ test ] -fn reflect_struct_with_lifetime() -{ - use reflect::Entity; - - // assumptions - a_id!( core::any::TypeId::of::< &'static str >(), core::any::TypeId::of::< &str >() ); - - // structure - let x = 1; - let z = "3"; - let ins = Struct1 - { - f1 : &x, - f2 : 2, - f3 : &z, - }; - - // for information - println!( "Struct1 : {:?}", reflect( &ins ).type_id() ); - println!( "Struct1.f1 : {:?}", reflect( &ins ).elements().next().unwrap().val.type_id() ); - println!( "Struct1.f2 : {:?}", reflect( &ins ).elements().skip( 1 ).next().unwrap().val.type_id() ); - println!( "Struct1.f3 : {:?}", reflect( &ins ).elements().skip( 2 ).next().unwrap().val.type_id() ); - - println!( "i32.type_id : {:?}", reflect( &1i32 ).type_id() ); - println!( "i32.type_name : {:?}", reflect( &1i32 ).type_name() ); - println!( "&i32.type_id : {:?}", reflect( &&1i32 ).type_id() ); - println!( "&i32.type_name : {:?}", reflect( &&1i32 ).type_name() ); - - // inspection of structure - a_id!( reflect::reflect( &ins ).is_container(), true ); - a_id!( reflect::reflect( &ins ).len(), 3 ); - a_id!( reflect::reflect( &ins ).type_name(), "derive_tests::inc::reflect_struct_with_lifetime_manual_test::Struct1" ); - a_id!( reflect::reflect( &ins ).type_id(), core::any::TypeId::of::< Struct1< 'static, 'static > >() ); - let names = reflect::reflect( &ins ).elements().map( | e | e.key ).collect::< Vec< _ > >(); - a_id!( names, vec![ reflect::Primitive::str( "f1" ), reflect::Primitive::str( "f2" ), reflect::Primitive::str( "f3" ) ] ); - let types = reflect::reflect( &ins ).elements().map( | e | e.val.type_name() ).collect::< Vec< _ > >(); - a_id!( types, vec![ "&i32", "i32", "&str" ] ); - - // inspection of a field - let f1 = reflect::reflect( &ins ).elements().next().unwrap(); - a_id!( f1.key, reflect::Primitive::str( "f1" ) ); - a_id!( f1.val.is_container(), false ); - a_id!( f1.val.len(), 0 ); - a_id!( f1.val.type_name(), "&i32" ); - a_id!( f1.val.type_id(), core::any::TypeId::of::< &'static i32 >() ); - a_id!( f1.val.elements().collect::< Vec< _ > >(), vec![] ); - -} diff --git a/module/core/derive_tools_meta/Cargo.toml b/module/core/derive_tools_meta/Cargo.toml index 5eba0b3302..747a5fa892 100644 --- a/module/core/derive_tools_meta/Cargo.toml +++ b/module/core/derive_tools_meta/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "derive_tools_meta" -version = "0.18.0" +version = "0.22.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", @@ -35,6 +35,7 @@ default = [ "derive_deref_mut", "derive_deref", "derive_from", + "derive_new", "derive_inner_from", "derive_as_ref", "derive_as_mut", @@ -45,25 +46,29 @@ full = [ "derive_deref_mut", "derive_deref", "derive_from", + "derive_new", "derive_inner_from", "derive_as_ref", "derive_as_mut", "derive_variadic_from", ] -enabled = [] +enabled = [ "macro_tools/enabled", "iter_tools/enabled", "former_types/enabled" ] derive_as_mut = [] derive_as_ref = [] derive_deref = [] derive_deref_mut = [] derive_from = [] +derive_new = [] derive_inner_from = [] derive_variadic_from = [] [dependencies] +# xxx : qqq : optimize features set macro_tools = { workspace = true, features = [ "full" ] } iter_tools = { workspace = true, features = [ "full" ] } -# xxx : qqq : optimize features set +former_types = { workspace = true, features = [ "types_component_assign" ] } +const_format = { version = "0.2.32" } [dev-dependencies] test_tools = { workspace = true } diff --git a/module/core/derive_tools_meta/src/derive.rs b/module/core/derive_tools_meta/src/derive.rs index 4f5242d2be..5008fe2fab 100644 --- a/module/core/derive_tools_meta/src/derive.rs +++ b/module/core/derive_tools_meta/src/derive.rs @@ -20,6 +20,8 @@ pub mod deref_mut; pub mod from; #[ cfg( feature = "derive_inner_from" ) ] pub mod inner_from; +#[ cfg( feature = "derive_new" ) ] +pub mod new; #[ cfg( feature = "derive_variadic_from" ) ] pub mod variadic_from; #[ cfg( feature = "derive_reflect" ) ] diff --git a/module/core/derive_tools_meta/src/derive/as_mut.rs b/module/core/derive_tools_meta/src/derive/as_mut.rs index 3a9cda3a12..5b51d648ae 100644 --- a/module/core/derive_tools_meta/src/derive/as_mut.rs +++ b/module/core/derive_tools_meta/src/derive/as_mut.rs @@ -1,12 +1,14 @@ use super::*; -use macro_tools::{ type_struct, Result }; +use macro_tools::{ attr, diag, item_struct, Result }; pub fn as_mut( input : proc_macro::TokenStream ) -> Result< proc_macro2::TokenStream > { - let parsed = syn::parse::< type_struct::TypeStructParsed >( input )?; - let field_type = parsed.first_field_type()?; - let item_name = parsed.item_name; + let original_input = input.clone(); + let parsed = syn::parse::< syn::ItemStruct >( input )?; + let has_debug = attr::has_debug( parsed.attrs.iter() )?; + let item_name = &parsed.ident; + let field_type = item_struct::first_field_type( &parsed )?; let result = qt! { @@ -19,5 +21,11 @@ pub fn as_mut( input : proc_macro::TokenStream ) -> Result< proc_macro2::TokenSt } }; + if has_debug + { + let about = format!( "derive : AsMut\nstructure : {item_name}" ); + diag::report_print( about, &original_input, &result ); + } + Ok( result ) } diff --git a/module/core/derive_tools_meta/src/derive/as_ref.rs b/module/core/derive_tools_meta/src/derive/as_ref.rs index 506d4f25e6..dba4eacacf 100644 --- a/module/core/derive_tools_meta/src/derive/as_ref.rs +++ b/module/core/derive_tools_meta/src/derive/as_ref.rs @@ -1,14 +1,16 @@ use super::*; -use macro_tools::{ type_struct, Result }; +use macro_tools::{ attr, diag, item_struct, Result }; // pub fn as_ref( input : proc_macro::TokenStream ) -> Result< proc_macro2::TokenStream > { - let parsed = syn::parse::< type_struct::TypeStructParsed >( input )?; - let field_type = parsed.first_field_type()?; - let item_name = parsed.item_name; + let original_input = input.clone(); + let parsed = syn::parse::< syn::ItemStruct >( input )?; + let has_debug = attr::has_debug( parsed.attrs.iter() )?; + let field_type = item_struct::first_field_type( &parsed )?; + let item_name = &parsed.ident; let result = qt! { @@ -21,5 +23,11 @@ pub fn as_ref( input : proc_macro::TokenStream ) -> Result< proc_macro2::TokenSt } }; + if has_debug + { + let about = format!( "derive : AsRef\nstructure : {item_name}" ); + diag::report_print( about, &original_input, &result ); + } + Ok( result ) } diff --git a/module/core/derive_tools_meta/src/derive/deref.rs b/module/core/derive_tools_meta/src/derive/deref.rs index cf10b9630c..3d0640bf9f 100644 --- a/module/core/derive_tools_meta/src/derive/deref.rs +++ b/module/core/derive_tools_meta/src/derive/deref.rs @@ -1,12 +1,15 @@ use super::*; -use macro_tools::{ type_struct, Result }; +use macro_tools::{ attr, diag, item_struct, Result }; pub fn deref( input : proc_macro::TokenStream ) -> Result< proc_macro2::TokenStream > { - let parsed = syn::parse::< type_struct::TypeStructParsed >( input )?; - let field_type = parsed.first_field_type()?; - let item_name = parsed.item_name; + let original_input = input.clone(); + let parsed = syn::parse::< syn::ItemStruct >( input )?; + let has_debug = attr::has_debug( parsed.attrs.iter() )?; + + let item_name = &parsed.ident; + let field_type = item_struct::first_field_type( &parsed )?; let result = qt! { @@ -21,5 +24,11 @@ pub fn deref( input : proc_macro::TokenStream ) -> Result< proc_macro2::TokenStr } }; + if has_debug + { + let about = format!( "derive : Deref\nstructure : {item_name}" ); + diag::report_print( about, &original_input, &result ); + } + Ok( result ) } diff --git a/module/core/derive_tools_meta/src/derive/deref_mut.rs b/module/core/derive_tools_meta/src/derive/deref_mut.rs index 0fd71ca1f6..20b778e448 100644 --- a/module/core/derive_tools_meta/src/derive/deref_mut.rs +++ b/module/core/derive_tools_meta/src/derive/deref_mut.rs @@ -1,13 +1,15 @@ use super::*; -use macro_tools::{ type_struct, Result }; +use macro_tools::{ attr, diag, Result }; // pub fn deref_mut( input : proc_macro::TokenStream ) -> Result< proc_macro2::TokenStream > { - let parsed = syn::parse::< type_struct::TypeStructParsed >( input )?; - let item_name = parsed.item_name; + let original_input = input.clone(); + let parsed = syn::parse::< syn::ItemStruct >( input )?; + let has_debug = attr::has_debug( parsed.attrs.iter() )?; + let item_name = &parsed.ident; let result = qt! { @@ -21,5 +23,11 @@ pub fn deref_mut( input : proc_macro::TokenStream ) -> Result< proc_macro2::Toke } }; + if has_debug + { + let about = format!( "derive : DerefMut\nstructure : {item_name}" ); + diag::report_print( about, &original_input, &result ); + } + Ok( result ) } diff --git a/module/core/derive_tools_meta/src/derive/from.rs b/module/core/derive_tools_meta/src/derive/from.rs index 43b5e727aa..88783c21e6 100644 --- a/module/core/derive_tools_meta/src/derive/from.rs +++ b/module/core/derive_tools_meta/src/derive/from.rs @@ -1,44 +1,195 @@ use super::*; -use macro_tools::{ type_struct, Result }; +use macro_tools:: +{ + attr, + diag, + generic_params, + item_struct, + struct_like::StructLike, + Result, +}; + +mod field_attributes; +use field_attributes::*; +mod item_attributes; +use item_attributes::*; // pub fn from( input : proc_macro::TokenStream ) -> Result< proc_macro2::TokenStream > { - let parsed = syn::parse::< type_struct::TypeStructParsed >( input )?; - let field_types = parsed.field_types(); - let field_names = parsed.field_names(); - let item_name = parsed.item_name.clone(); - let result = - match ( field_types.len(), field_names ) + // use macro_tools::quote::ToTokens; + + let original_input = input.clone(); + let parsed = syn::parse::< StructLike >( input )?; + let has_debug = attr::has_debug( parsed.attrs().iter() )?; + let item_attrs = ItemAttributes::from_attrs( parsed.attrs().iter() )?; + let item_name = &parsed.ident(); + + let ( _generics_with_defaults, generics_impl, generics_ty, generics_where ) + = generic_params::decompose( &parsed.generics() ); + + let result = match parsed { - ( 0, _ ) => { generate_unit(item_name) }, - ( 1, Some( field_names ) ) => generate_from_single_field_named( &field_types[ 0 ], &field_names[ 0 ], item_name ), - ( 1, None ) => generate_from_single_field( &field_types[ 0 ], item_name ), - ( _, Some( field_names ) ) => generate_from_multiple_fields_named( &field_types, &field_names, item_name ), - ( _, None ) => generate_from_multiple_fields( &field_types, item_name ), + StructLike::Unit( ref item ) | StructLike::Struct( ref item ) => + { + + let mut field_types = item_struct::field_types( &item ); + let field_names = item_struct::field_names( &item ); + + match ( field_types.len(), field_names ) + { + ( 0, _ ) => + generate_unit + ( + item_name, + &generics_impl, + &generics_ty, + &generics_where, + ), + ( 1, Some( mut field_names ) ) => + generate_single_field_named + ( + item_name, + &generics_impl, + &generics_ty, + &generics_where, + field_names.next().unwrap(), + &field_types.next().unwrap(), + ), + ( 1, None ) => + generate_single_field + ( + item_name, + &generics_impl, + &generics_ty, + &generics_where, + &field_types.next().unwrap(), + ), + ( _, Some( field_names ) ) => + generate_multiple_fields_named + ( + item_name, + &generics_impl, + &generics_ty, + &generics_where, + field_names, + field_types, + ), + ( _, None ) => + generate_multiple_fields + ( + item_name, + &generics_impl, + &generics_ty, + &generics_where, + field_types, + ), + } + + }, + StructLike::Enum( ref item ) => + { + + // let mut map = std::collections::HashMap::new(); + // item.variants.iter().for_each( | variant | + // { + // map + // .entry( variant.fields.to_token_stream().to_string() ) + // .and_modify( | e | *e += 1 ) + // .or_insert( 1 ); + // }); + + let variants_result : Result< Vec< proc_macro2::TokenStream > > = item.variants.iter().map( | variant | + { + // don't do automatic off + // if map[ & variant.fields.to_token_stream().to_string() ] <= 1 + if true + { + variant_generate + ( + item_name, + &item_attrs, + &generics_impl, + &generics_ty, + &generics_where, + variant, + &original_input, + ) + } + else + { + Ok( qt!{} ) + } + }).collect(); + + let variants = variants_result?; + + qt! + { + #( #variants )* + } + }, }; + if has_debug + { + let about = format!( "derive : From\nstructure : {item_name}" ); + diag::report_print( about, &original_input, &result ); + } + Ok( result ) } -// qqq : document, add example of generated code -fn generate_from_single_field_named +// qqq : document, add example of generated code +fn generate_unit ( - field_type: &syn::Type, - field_name: &syn::Ident, - item_name: syn::Ident, -) -> proc_macro2::TokenStream + item_name : &syn::Ident, + generics_impl : &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, + generics_ty : &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, + generics_where: &syn::punctuated::Punctuated< syn::WherePredicate, syn::token::Comma >, +) +-> proc_macro2::TokenStream +{ + qt! + { + // impl From< () > for UnitStruct + impl< #generics_impl > From< () > for #item_name< #generics_ty > + where + #generics_where + { + #[ inline( always ) ] + fn from( src : () ) -> Self + { + Self + } + } + } +} + +// qqq : document, add example of generated code +fn generate_single_field_named +( + item_name : &syn::Ident, + generics_impl : &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, + generics_ty : &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, + generics_where: &syn::punctuated::Punctuated< syn::WherePredicate, syn::token::Comma >, + field_name : &syn::Ident, + field_type : &syn::Type, +) +-> proc_macro2::TokenStream { qt! { #[ automatically_derived ] // impl From < i32 > for MyStruct - impl From< #field_type > for #item_name + impl< #generics_impl > From< #field_type > for #item_name< #generics_ty > + where + #generics_where { #[ inline( always ) ] - // fn from( src: i32 ) -> Self - fn from( src: #field_type ) -> Self + // fn from( src : i32 ) -> Self + fn from( src : #field_type ) -> Self { // Self { a: src } Self { #field_name: src } @@ -47,108 +198,215 @@ fn generate_from_single_field_named } } -// qqq : document, add example of generated code -fn generate_from_single_field +// qqq : document, add example of generated code +fn generate_single_field ( - field_type: &syn::Type, - item_name: syn::Ident, -) -> proc_macro2::TokenStream + item_name : &syn::Ident, + generics_impl : &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, + generics_ty : &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, + generics_where: &syn::punctuated::Punctuated< syn::WherePredicate, syn::token::Comma >, + field_type : &syn::Type, +) +-> proc_macro2::TokenStream { + qt! { #[automatically_derived] // impl From< bool > for IsTransparent - impl From< #field_type > for #item_name + impl< #generics_impl > From< #field_type > for #item_name< #generics_ty > + where + #generics_where { #[ inline( always ) ] - // fn from( src: bool ) -> Self - fn from( src: #field_type ) -> Self + // fn from( src : bool ) -> Self + fn from( src : #field_type ) -> Self { - // Self(src) - Self(src) + // Self( src ) + Self( src ) } } } } -// qqq : document, add example of generated code -fn generate_from_multiple_fields_named +// qqq : document, add example of generated code +fn generate_multiple_fields_named< 'a > ( - field_types : &Vec< &syn::Type >, - field_names : &Vec< syn::Ident >, - item_name : syn::Ident -) -> proc_macro2::TokenStream + item_name : &syn::Ident, + generics_impl : &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, + generics_ty : &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, + generics_where: &syn::punctuated::Punctuated< syn::WherePredicate, syn::token::Comma >, + field_names : Box< dyn macro_tools::IterTrait< 'a, &'a syn::Ident > + '_ >, + field_types : impl macro_tools::IterTrait< 'a, &'a syn::Type >, +) +-> proc_macro2::TokenStream { - let params: Vec< proc_macro2::TokenStream > = field_names - .iter() - .enumerate() - .map(| ( index, field_name ) | - { - let index = index.to_string().parse::< proc_macro2::TokenStream >().unwrap(); - qt! { #field_name : src.#index } - }) - .collect(); + let params : Vec< proc_macro2::TokenStream > = field_names + .enumerate() + .map(| ( index, field_name ) | + { + let index = index.to_string().parse::< proc_macro2::TokenStream >().unwrap(); + qt! { #field_name : src.#index } + }) + .collect(); + + let field_types : Vec< _ > = field_types.collect(); qt! { // impl From< (i32, bool) > for StructNamedFields - impl From< (#(#field_types), *) > for #item_name + impl< #generics_impl > From< (# ( #field_types ),* ) > for #item_name< #generics_ty > + where + #generics_where { #[ inline( always ) ] - // fn from( src: (i32, bool) ) -> Self - fn from( src: (#(#field_types), *) ) -> Self + // fn from( src : (i32, bool) ) -> Self + fn from( src : ( #( #field_types ),* ) ) -> Self { // StructNamedFields{ a: src.0, b: src.1 } - #item_name { #(#params), * } + #item_name { #(#params),* } } } } + } -// qqq : document, add example of generated code -fn generate_from_multiple_fields +// qqq : document, add example of generated code +fn generate_multiple_fields< 'a > ( - field_types : &Vec< &syn::Type >, - item_name: syn::Ident, -) -> proc_macro2::TokenStream + item_name : &syn::Ident, + generics_impl : &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, + generics_ty : &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, + generics_where: &syn::punctuated::Punctuated< syn::WherePredicate, syn::token::Comma >, + field_types : impl macro_tools::IterTrait< 'a, &'a macro_tools::syn::Type >, +) +-> proc_macro2::TokenStream { - let params: Vec< proc_macro2::TokenStream > = ( 0..field_types.len() ) + + let params : Vec< proc_macro2::TokenStream > = ( 0..field_types.len() ) .map( | index | - { - let index = index.to_string().parse::< proc_macro2::TokenStream >().unwrap(); - qt!( src.#index ) - } ) + { + let index = index.to_string().parse::< proc_macro2::TokenStream >().unwrap(); + qt!( src.#index ) + }) .collect(); + let field_types : Vec< _ > = field_types.collect(); + qt! { // impl From< (i32, bool) > for StructWithManyFields - impl From< (#(#field_types), *) > for #item_name + impl< #generics_impl > From< (# ( #field_types ),* ) > for #item_name< #generics_ty > + where + #generics_where { #[ inline( always ) ] - // fn from( src: (i32, bool) ) -> Self - fn from( src: (#(#field_types), *) ) -> Self + // fn from( src : (i32, bool) ) -> Self + fn from( src : ( #( #field_types ),* ) ) -> Self { // StructWithManyFields( src.0, src.1 ) - #item_name( #(#params), *) + #item_name( #( #params ),* ) } } } } -// qqq : document, add example of generated code -fn generate_unit( item_name: syn::Ident ) -> proc_macro2::TokenStream +// qqq : document, add example of generated code +fn variant_generate +( + item_name : &syn::Ident, + item_attrs : &ItemAttributes, + generics_impl : &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, + generics_ty : &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, + generics_where: &syn::punctuated::Punctuated< syn::WherePredicate, syn::token::Comma >, + variant : &syn::Variant, + original_input : &proc_macro::TokenStream, +) +-> Result< proc_macro2::TokenStream > { - qt! + let variant_name = &variant.ident; + let fields = &variant.fields; + let attrs = FieldAttributes::from_attrs( variant.attrs.iter() )?; + + if !attrs.config.enabled.value( item_attrs.config.enabled.value( true ) ) { - // impl From< () > for UnitStruct - impl From< () > for #item_name + return Ok( qt!{} ) + } + + if fields.len() <= 0 + { + return Ok( qt!{} ) + } + + let ( args, use_src ) = if fields.len() == 1 + { + let field = fields.iter().next().unwrap(); + ( + qt!{ #field }, + qt!{ src }, + ) + } + else + { + let src_i = ( 0..fields.len() ).map( | e | { - #[ inline( always ) ] - fn from( src: () ) -> Self + let i = syn::Index::from( e ); + qt!{ src.#i, } + }); + ( + qt!{ #fields }, + qt!{ #( #src_i )* }, + // qt!{ src.0, src.1 }, + ) + }; + + // qqq : make `debug` working for all branches + if attrs.config.debug.value( false ) + { + let debug = format! + ( + r#" +#[ automatically_derived ] +impl< {0} > From< {args} > for {item_name}< {1} > +where + {2} +{{ + #[ inline ] + fn from( src : {args} ) -> Self + {{ + Self::{variant_name}( {use_src} ) + }} +}} + "#, + format!( "{}", qt!{ #generics_impl } ), + format!( "{}", qt!{ #generics_ty } ), + format!( "{}", qt!{ #generics_where } ), + ); + let about = format! + ( +r#"derive : From +item : {item_name} +field : {variant_name}"#, + ); + diag::report_print( about, original_input, debug ); + } + + Ok + ( + qt! + { + #[ automatically_derived ] + impl< #generics_impl > From< #args > for #item_name< #generics_ty > + where + #generics_where { - Self + #[ inline ] + fn from( src : #args ) -> Self + { + Self::#variant_name( #use_src ) + } } } - } -} \ No newline at end of file + ) + +} diff --git a/module/core/derive_tools_meta/src/derive/from/field_attributes.rs b/module/core/derive_tools_meta/src/derive/from/field_attributes.rs new file mode 100644 index 0000000000..8ff1e9f56f --- /dev/null +++ b/module/core/derive_tools_meta/src/derive/from/field_attributes.rs @@ -0,0 +1,252 @@ +use super::*; +use macro_tools:: +{ + Result, + AttributeComponent, + AttributePropertyComponent, + AttributePropertyOptionalSingletone, +}; + +use former_types::Assign; + +/// +/// Attributes of a field / variant +/// + +/// Represents the attributes of a struct. Aggregates all its attributes. +#[ derive( Debug, Default ) ] +pub struct FieldAttributes +{ + /// Attribute for customizing generated code. + pub config : FieldAttributeConfig, +} + +impl FieldAttributes +{ + + pub fn from_attrs< 'a >( attrs : impl Iterator< Item = &'a syn::Attribute > ) -> Result< Self > + { + let mut result = Self::default(); + + let error = | attr : &syn::Attribute | -> syn::Error + { + let known_attributes = const_format::concatcp! + ( + "Known attirbutes are : ", + "debug", + ", ", FieldAttributeConfig::KEYWORD, + ".", + ); + syn_err! + ( + attr, + "Expects an attribute of format '#[ attribute( key1 = val1, key2 = val2 ) ]'\n {known_attributes}\n But got: '{}'", + qt!{ #attr } + ) + }; + + for attr in attrs + { + + let key_ident = attr.path().get_ident().ok_or_else( || error( attr ) )?; + let key_str = format!( "{}", key_ident ); + + // attributes does not have to be known + // if attr::is_standard( &key_str ) + // { + // continue; + // } + + match key_str.as_ref() + { + FieldAttributeConfig::KEYWORD => result.assign( FieldAttributeConfig::from_meta( attr )? ), + "debug" => {}, + _ => {}, + // _ => return Err( error( attr ) ), + } + } + + Ok( result ) + } + +} + +/// +/// Attribute to hold parameters of forming for a specific field or variant. +/// For example to avoid code From generation for it. +/// +/// `#[ from( on ) ]` +/// + +#[ derive( Debug, Default ) ] +pub struct FieldAttributeConfig +{ + /// Specifies whether we should generate From implementation for the field. + /// Can be altered using `on` and `off` attributes + pub enabled : AttributePropertyEnabled, + /// Specifies whether to print a sketch of generated `From` or not. + /// Defaults to `false`, which means no code is printed unless explicitly requested. + pub debug : AttributePropertyDebug, + // qqq : apply debug properties to all brenches, not only enums +} + +impl AttributeComponent for FieldAttributeConfig +{ + const KEYWORD : &'static str = "from"; + + fn from_meta( attr : &syn::Attribute ) -> Result< Self > + { + match attr.meta + { + syn::Meta::List( ref meta_list ) => + { + return syn::parse2::< FieldAttributeConfig >( meta_list.tokens.clone() ); + }, + syn::Meta::Path( ref _path ) => + { + return Ok( Default::default() ) + }, + _ => return_syn_err!( attr, "Expects an attribute of format `#[ from( on ) ]`. \nGot: {}", qt!{ #attr } ), + } + } + +} + +impl< IntoT > Assign< FieldAttributeConfig, IntoT > for FieldAttributes +where + IntoT : Into< FieldAttributeConfig >, +{ + #[ inline( always ) ] + fn assign( &mut self, component : IntoT ) + { + self.config.assign( component.into() ); + } +} + +impl< IntoT > Assign< FieldAttributeConfig, IntoT > for FieldAttributeConfig +where + IntoT : Into< FieldAttributeConfig >, +{ + #[ inline( always ) ] + fn assign( &mut self, component : IntoT ) + { + let component = component.into(); + self.enabled.assign( component.enabled ); + self.debug.assign( component.debug ); + } +} + +impl< IntoT > Assign< AttributePropertyEnabled, IntoT > for FieldAttributeConfig +where + IntoT : Into< AttributePropertyEnabled >, +{ + #[ inline( always ) ] + fn assign( &mut self, component : IntoT ) + { + self.enabled = component.into(); + } +} + +impl< IntoT > Assign< AttributePropertyDebug, IntoT > for FieldAttributeConfig +where + IntoT : Into< AttributePropertyDebug >, +{ + #[ inline( always ) ] + fn assign( &mut self, component : IntoT ) + { + self.debug = component.into(); + } +} + +impl syn::parse::Parse for FieldAttributeConfig +{ + fn parse( input : syn::parse::ParseStream< '_ > ) -> syn::Result< Self > + { + let mut result = Self::default(); + + let error = | ident : &syn::Ident | -> syn::Error + { + let known = const_format::concatcp! + ( + "Known entries of attribute ", FieldAttributeConfig::KEYWORD, " are : ", + AttributePropertyDebug::KEYWORD, + ", ", EnabledMarker::KEYWORD_ON, + ", ", EnabledMarker::KEYWORD_OFF, + ".", + ); + syn_err! + ( + ident, + r#"Expects an attribute of format '#[ from( on ) ]' + {known} + But got: '{}' +"#, + qt!{ #ident } + ) + }; + + while !input.is_empty() + { + let lookahead = input.lookahead1(); + if lookahead.peek( syn::Ident ) + { + let ident : syn::Ident = input.parse()?; + match ident.to_string().as_str() + { + AttributePropertyDebug::KEYWORD => result.assign( AttributePropertyDebug::from( true ) ), + EnabledMarker::KEYWORD_ON => result.assign( AttributePropertyEnabled::from( true ) ), + EnabledMarker::KEYWORD_OFF => result.assign( AttributePropertyEnabled::from( false ) ), + _ => return Err( error( &ident ) ), + } + } + else + { + return Err( lookahead.error() ); + } + + // Optional comma handling + if input.peek( syn::Token![ , ] ) + { + input.parse::< syn::Token![ , ] >()?; + } + } + + Ok( result ) + } +} + +// == attribute properties + +/// Marker type for attribute property to specify whether to provide a generated code as a hint. +/// Defaults to `false`, which means no debug is provided unless explicitly requested. +#[ derive( Debug, Default, Clone, Copy ) ] +pub struct AttributePropertyDebugMarker; + +impl AttributePropertyComponent for AttributePropertyDebugMarker +{ + const KEYWORD : &'static str = "debug"; +} + +/// Specifies whether to provide a generated code as a hint. +/// Defaults to `false`, which means no debug is provided unless explicitly requested. +pub type AttributePropertyDebug = AttributePropertyOptionalSingletone< AttributePropertyDebugMarker >; + +// = + +/// Marker type for attribute property to indicates whether `From` implementation for fields/variants should be generated. +#[ derive( Debug, Default, Clone, Copy ) ] +pub struct EnabledMarker; + +impl EnabledMarker +{ + /// Keywords for parsing this attribute property. + pub const KEYWORD_OFF : &'static str = "off"; + /// Keywords for parsing this attribute property. + pub const KEYWORD_ON : &'static str = "on"; +} + +/// Specifies whether `From` implementation for fields/variants should be generated. +/// Can be altered using `on` and `off` attributes. But default it's `on`. +pub type AttributePropertyEnabled = AttributePropertyOptionalSingletone< EnabledMarker >; + +// == diff --git a/module/core/derive_tools_meta/src/derive/from/item_attributes.rs b/module/core/derive_tools_meta/src/derive/from/item_attributes.rs new file mode 100644 index 0000000000..78cc32f2d4 --- /dev/null +++ b/module/core/derive_tools_meta/src/derive/from/item_attributes.rs @@ -0,0 +1,201 @@ +use super::*; +use macro_tools:: +{ + Result, + AttributeComponent, +}; + +use former_types::Assign; + +/// +/// Attributes of the whole tiem +/// + +/// Represents the attributes of a struct. Aggregates all its attributes. +#[ derive( Debug, Default ) ] +pub struct ItemAttributes +{ + /// Attribute for customizing generated code. + pub config : ItemAttributeConfig, +} + +impl ItemAttributes +{ + + pub fn from_attrs< 'a >( attrs : impl Iterator< Item = &'a syn::Attribute > ) -> Result< Self > + { + let mut result = Self::default(); + + let error = | attr : &syn::Attribute | -> syn::Error + { + let known_attributes = const_format::concatcp! + ( + "Known attirbutes are : ", + "debug", + ", ", ItemAttributeConfig::KEYWORD, + ".", + ); + syn_err! + ( + attr, + "Expects an attribute of format '#[ attribute( key1 = val1, key2 = val2 ) ]'\n {known_attributes}\n But got: '{}'", + qt!{ #attr } + ) + }; + + for attr in attrs + { + + let key_ident = attr.path().get_ident().ok_or_else( || error( attr ) )?; + let key_str = format!( "{}", key_ident ); + + // attributes does not have to be known + // if attr::is_standard( &key_str ) + // { + // continue; + // } + + match key_str.as_ref() + { + ItemAttributeConfig::KEYWORD => result.assign( ItemAttributeConfig::from_meta( attr )? ), + "debug" => {} + _ => {}, + // _ => return Err( error( attr ) ), + // attributes does not have to be known + } + } + + Ok( result ) + } + +} + +/// +/// Attribute to hold parameters of forming for a specific field or variant. +/// For example to avoid code From generation for it. +/// +/// `#[ from( on ) ]` +/// + +#[ derive( Debug, Default ) ] +pub struct ItemAttributeConfig +{ + /// Specifies whether `From` implementation for fields/variants should be generated by default. + /// Can be altered using `on` and `off` attributes. But default it's `on`. + /// `#[ from( on ) ]` - `From` is generated unless `off` for the field/variant is explicitly specified. + /// `#[ from( off ) ]` - `From` is not generated unless `on` for the field/variant is explicitly specified. + pub enabled : AttributePropertyEnabled, +} + +impl AttributeComponent for ItemAttributeConfig +{ + const KEYWORD : &'static str = "from"; + + fn from_meta( attr : &syn::Attribute ) -> Result< Self > + { + match attr.meta + { + syn::Meta::List( ref meta_list ) => + { + return syn::parse2::< ItemAttributeConfig >( meta_list.tokens.clone() ); + }, + syn::Meta::Path( ref _path ) => + { + return Ok( Default::default() ) + }, + _ => return_syn_err!( attr, "Expects an attribute of format `#[ from( on ) ]`. \nGot: {}", qt!{ #attr } ), + } + } + +} + +impl< IntoT > Assign< ItemAttributeConfig, IntoT > for ItemAttributes +where + IntoT : Into< ItemAttributeConfig >, +{ + #[ inline( always ) ] + fn assign( &mut self, component : IntoT ) + { + self.config.assign( component.into() ); + } +} + +impl< IntoT > Assign< ItemAttributeConfig, IntoT > for ItemAttributeConfig +where + IntoT : Into< ItemAttributeConfig >, +{ + #[ inline( always ) ] + fn assign( &mut self, component : IntoT ) + { + let component = component.into(); + self.enabled.assign( component.enabled ); + } +} + +impl< IntoT > Assign< AttributePropertyEnabled, IntoT > for ItemAttributeConfig +where + IntoT : Into< AttributePropertyEnabled >, +{ + #[ inline( always ) ] + fn assign( &mut self, component : IntoT ) + { + self.enabled = component.into(); + } +} + +impl syn::parse::Parse for ItemAttributeConfig +{ + fn parse( input : syn::parse::ParseStream< '_ > ) -> syn::Result< Self > + { + let mut result = Self::default(); + + let error = | ident : &syn::Ident | -> syn::Error + { + let known = const_format::concatcp! + ( + "Known entries of attribute ", ItemAttributeConfig::KEYWORD, " are : ", + EnabledMarker::KEYWORD_ON, + ", ", EnabledMarker::KEYWORD_OFF, + ".", + ); + syn_err! + ( + ident, + r#"Expects an attribute of format '#[ from( off ) ]' + {known} + But got: '{}' +"#, + qt!{ #ident } + ) + }; + + while !input.is_empty() + { + let lookahead = input.lookahead1(); + if lookahead.peek( syn::Ident ) + { + let ident : syn::Ident = input.parse()?; + match ident.to_string().as_str() + { + EnabledMarker::KEYWORD_ON => result.assign( AttributePropertyEnabled::from( true ) ), + EnabledMarker::KEYWORD_OFF => result.assign( AttributePropertyEnabled::from( false ) ), + _ => return Err( error( &ident ) ), + } + } + else + { + return Err( lookahead.error() ); + } + + // Optional comma handling + if input.peek( syn::Token![ , ] ) + { + input.parse::< syn::Token![ , ] >()?; + } + } + + Ok( result ) + } +} + +// == diff --git a/module/core/derive_tools_meta/src/derive/inner_from.rs b/module/core/derive_tools_meta/src/derive/inner_from.rs index bf80c7dd51..2226df43d8 100644 --- a/module/core/derive_tools_meta/src/derive/inner_from.rs +++ b/module/core/derive_tools_meta/src/derive/inner_from.rs @@ -1,36 +1,39 @@ use super::*; -use macro_tools::{ type_struct, Result }; +use macro_tools::{ attr, diag, item_struct, Result }; // pub fn inner_from( input : proc_macro::TokenStream ) -> Result< proc_macro2::TokenStream > { - let parsed = syn::parse::< type_struct::TypeStructParsed >( input )?; - let field_types = parsed.field_types(); - let field_names = parsed.field_names(); - let item_name = parsed.item_name.clone(); + let original_input = input.clone(); + let parsed = syn::parse::< syn::ItemStruct >( input )?; + let has_debug = attr::has_debug( parsed.attrs.iter() )?; + let item_name = &parsed.ident; + + let mut field_types = item_struct::field_types( &parsed ); + let field_names = item_struct::field_names( &parsed ); let result = match ( field_types.len(), field_names ) { ( 0, _ ) => unit( item_name ), - ( 1, Some( field_names ) ) => + ( 1, Some( mut field_names ) ) => { - let field_name = field_names.get( 0 ).unwrap(); - let field_type = field_types.get( 0 ).unwrap(); + let field_name = field_names.next().unwrap(); + let field_type = field_types.next().unwrap(); from_impl_named( item_name, field_type, field_name ) } ( 1, None ) => { - let field_type = field_types.get( 0 ).unwrap(); + let field_type = field_types.next().unwrap(); from_impl( item_name, field_type ) } ( _, Some( field_names ) ) => { - let params : Vec< proc_macro2::TokenStream > = field_names.iter() + let params : Vec< proc_macro2::TokenStream > = field_names .map( | field_name | qt! { src.#field_name } ) .collect(); - from_impl_multiple_fields( item_name, &field_types, ¶ms ) + from_impl_multiple_fields( item_name, field_types, ¶ms ) } ( _, None ) => { @@ -41,16 +44,23 @@ pub fn inner_from( input : proc_macro::TokenStream ) -> Result< proc_macro2::Tok qt! { src.#index } }) .collect(); - from_impl_multiple_fields( item_name, &field_types, ¶ms ) + from_impl_multiple_fields( item_name, field_types, ¶ms ) } }; + + if has_debug + { + let about = format!( "derive : InnerFrom\nstructure : {item_name}" ); + diag::report_print( about, &original_input, &result ); + } + Ok( result ) } -// qqq : document, add example of generated code +// qqq : document, add example of generated code fn from_impl_named ( - item_name : syn::Ident, + item_name : &syn::Ident, field_type : &syn::Type, field_name : &syn::Ident, ) -> proc_macro2::TokenStream @@ -63,8 +73,8 @@ fn from_impl_named impl From< #item_name > for #field_type { #[ inline( always ) ] - // fm from( src: MyStruct ) -> Self - fn from( src: #item_name ) -> Self + // fm from( src : MyStruct ) -> Self + fn from( src : #item_name ) -> Self { // src.a src.#field_name @@ -73,10 +83,10 @@ fn from_impl_named } } -// qqq : document, add example of generated code +// qqq : document, add example of generated code fn from_impl ( - item_name : syn::Ident, + item_name : &syn::Ident, field_type : &syn::Type, ) -> proc_macro2::TokenStream { @@ -88,8 +98,8 @@ fn from_impl impl From< #item_name > for #field_type { #[ inline( always ) ] - // fn from( src: IsTransparent ) -> Self - fn from( src: #item_name ) -> Self + // fn from( src : IsTransparent ) -> Self + fn from( src : #item_name ) -> Self { src.0 } @@ -97,11 +107,11 @@ fn from_impl } } -// qqq : document, add example of generated code -fn from_impl_multiple_fields +// qqq : document, add example of generated code +fn from_impl_multiple_fields< 'a > ( - item_name : syn::Ident, - field_types : &Vec< &syn::Type >, + item_name : &syn::Ident, + field_types : impl macro_tools::IterTrait< 'a, &'a macro_tools::syn::Type >, params : &Vec< proc_macro2::TokenStream >, ) -> proc_macro2::TokenStream { @@ -113,8 +123,8 @@ fn from_impl_multiple_fields impl From< #item_name > for ( #(#field_types), *) { #[ inline( always ) ] - // fn from( src: StructWithManyFields ) -> Self - fn from( src: #item_name ) -> Self + // fn from( src : StructWithManyFields ) -> Self + fn from( src : #item_name ) -> Self { //( src.0, src.1 ) (#(#params), *) @@ -123,8 +133,8 @@ fn from_impl_multiple_fields } } -// qqq : document, add example of generated code -fn unit( item_name : syn::Ident ) -> proc_macro2::TokenStream +// qqq : document, add example of generated code +fn unit( item_name : &syn::Ident ) -> proc_macro2::TokenStream { qt! { @@ -134,8 +144,8 @@ fn unit( item_name : syn::Ident ) -> proc_macro2::TokenStream impl From< #item_name > for () { #[ inline( always ) ] - // fn from( src: UnitStruct ) -> () - fn from( src: #item_name ) -> () + // fn from( src : UnitStruct ) -> () + fn from( src : #item_name ) -> () { () } diff --git a/module/core/derive_tools_meta/src/derive/new.rs b/module/core/derive_tools_meta/src/derive/new.rs new file mode 100644 index 0000000000..4bfb0761bb --- /dev/null +++ b/module/core/derive_tools_meta/src/derive/new.rs @@ -0,0 +1,403 @@ +use super::*; +use macro_tools:: +{ + attr, + diag, + generic_params, + item_struct, + struct_like::StructLike, + Result, +}; + +#[ path = "from/field_attributes.rs" ] +mod field_attributes; +use field_attributes::*; +#[ path = "from/item_attributes.rs" ] +mod item_attributes; +use item_attributes::*; + +// + +// xxx : qqq : implement +pub fn new( input : proc_macro::TokenStream ) -> Result< proc_macro2::TokenStream > +{ + // use macro_tools::quote::ToTokens; + + let original_input = input.clone(); + let parsed = syn::parse::< StructLike >( input )?; + let has_debug = attr::has_debug( parsed.attrs().iter() )?; + let item_attrs = ItemAttributes::from_attrs( parsed.attrs().iter() )?; + let item_name = &parsed.ident(); + + let ( _generics_with_defaults, generics_impl, generics_ty, generics_where ) + = generic_params::decompose( &parsed.generics() ); + + let result = match parsed + { + StructLike::Unit( ref item ) | StructLike::Struct( ref item ) => + { + + let mut field_types = item_struct::field_types( &item ); + let field_names = item_struct::field_names( &item ); + + match ( field_types.len(), field_names ) + { + ( 0, _ ) => + generate_unit + ( + item_name, + &generics_impl, + &generics_ty, + &generics_where, + ), + ( 1, Some( mut field_names ) ) => + generate_single_field_named + ( + item_name, + &generics_impl, + &generics_ty, + &generics_where, + field_names.next().unwrap(), + &field_types.next().unwrap(), + ), + ( 1, None ) => + generate_single_field + ( + item_name, + &generics_impl, + &generics_ty, + &generics_where, + &field_types.next().unwrap(), + ), + ( _, Some( field_names ) ) => + generate_multiple_fields_named + ( + item_name, + &generics_impl, + &generics_ty, + &generics_where, + field_names, + field_types, + ), + ( _, None ) => + generate_multiple_fields + ( + item_name, + &generics_impl, + &generics_ty, + &generics_where, + field_types, + ), + } + + }, + StructLike::Enum( ref item ) => + { + + let variants_result : Result< Vec< proc_macro2::TokenStream > > = item.variants.iter().map( | variant | + { + variant_generate + ( + item_name, + &item_attrs, + &generics_impl, + &generics_ty, + &generics_where, + variant, + &original_input, + ) + }).collect(); + + let variants = variants_result?; + + qt! + { + #( #variants )* + } + }, + }; + + if has_debug + { + let about = format!( "derive : New\nstructure : {item_name}" ); + diag::report_print( about, &original_input, &result ); + } + + Ok( result ) +} + +// xxx : qqq : implement +// qqq : document, add example of generated code +fn generate_unit +( + item_name : &syn::Ident, + generics_impl : &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, + generics_ty : &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, + generics_where: &syn::punctuated::Punctuated< syn::WherePredicate, syn::token::Comma >, +) +-> proc_macro2::TokenStream +{ + qt! + { + // impl UnitStruct + impl< #generics_impl > #item_name< #generics_ty > + where + #generics_where + { + #[ inline( always ) ] + fn new() -> Self + { + Self + } + } + } +} + +// xxx : qqq : implement +// qqq : document, add example of generated code +fn generate_single_field_named +( + item_name : &syn::Ident, + generics_impl : &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, + generics_ty : &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, + generics_where: &syn::punctuated::Punctuated< syn::WherePredicate, syn::token::Comma >, + field_name : &syn::Ident, + field_type : &syn::Type, +) +-> proc_macro2::TokenStream +{ + qt! + { + #[ automatically_derived ] + // impl MyStruct + impl< #generics_impl > #item_name< #generics_ty > + where + #generics_where + { + #[ inline( always ) ] + // fn new( src : i32 ) -> Self + fn new( src : #field_type ) -> Self + { + // Self { a: src } + Self { #field_name: src } + } + } + } +} + +// xxx : qqq : implement +// qqq : document, add example of generated code +fn generate_single_field +( + item_name : &syn::Ident, + generics_impl : &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, + generics_ty : &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, + generics_where: &syn::punctuated::Punctuated< syn::WherePredicate, syn::token::Comma >, + field_type : &syn::Type, +) +-> proc_macro2::TokenStream +{ + + qt! + { + #[automatically_derived] + // impl IsTransparent + impl< #generics_impl > #item_name< #generics_ty > + where + #generics_where + { + #[ inline( always ) ] + // fn new( src : bool ) -> Self + fn new( src : #field_type ) -> Self + { + // Self( src ) + Self( src ) + } + } + } +} + +// xxx : qqq : implement +// qqq : document, add example of generated code +fn generate_multiple_fields_named< 'a > +( + item_name : &syn::Ident, + generics_impl : &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, + generics_ty : &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, + generics_where: &syn::punctuated::Punctuated< syn::WherePredicate, syn::token::Comma >, + field_names : Box< dyn macro_tools::IterTrait< 'a, &'a syn::Ident > + '_ >, + field_types : impl macro_tools::IterTrait< 'a, &'a syn::Type >, +) +-> proc_macro2::TokenStream +{ + + let params : Vec< proc_macro2::TokenStream > = field_names + .enumerate() + .map(| ( index, field_name ) | + { + let index = index.to_string().parse::< proc_macro2::TokenStream >().unwrap(); + qt! { #field_name : src.#index } + }) + .collect(); + + let field_types : Vec< _ > = field_types.collect(); + qt! + { + // impl StructNamedFields + impl< #generics_impl > #item_name< #generics_ty > + where + #generics_where + { + #[ inline( always ) ] + // fn new( src : ( i32, bool ) ) -> Self + fn new( src : ( #( #field_types ),* ) ) -> Self + { + // StructNamedFields{ a : src.0, b : src.1 } + #item_name { #(#params),* } + } + } + } + +} + +// xxx : qqq : implement +// qqq : document, add example of generated code +fn generate_multiple_fields< 'a > +( + item_name : &syn::Ident, + generics_impl : &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, + generics_ty : &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, + generics_where: &syn::punctuated::Punctuated< syn::WherePredicate, syn::token::Comma >, + field_types : impl macro_tools::IterTrait< 'a, &'a macro_tools::syn::Type >, +) +-> proc_macro2::TokenStream +{ + + let params : Vec< proc_macro2::TokenStream > = ( 0..field_types.len() ) + .map( | index | + { + let index = index.to_string().parse::< proc_macro2::TokenStream >().unwrap(); + qt!( src.#index ) + }) + .collect(); + + let field_types : Vec< _ > = field_types.collect(); + + qt! + { + // impl StructWithManyFields + impl< #generics_impl > #item_name< #generics_ty > + where + #generics_where + { + #[ inline( always ) ] + // fn new( src : (i32, bool) ) -> Self + fn new( src : ( #( #field_types ),* ) ) -> Self + { + // StructWithManyFields( src.0, src.1 ) + #item_name( #( #params ),* ) + } + } + } +} + +// xxx : qqq : implement +// qqq : document, add example of generated code +fn variant_generate +( + item_name : &syn::Ident, + item_attrs : &ItemAttributes, + generics_impl : &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, + generics_ty : &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, + generics_where: &syn::punctuated::Punctuated< syn::WherePredicate, syn::token::Comma >, + variant : &syn::Variant, + original_input : &proc_macro::TokenStream, +) +-> Result< proc_macro2::TokenStream > +{ + let variant_name = &variant.ident; + let fields = &variant.fields; + let attrs = FieldAttributes::from_attrs( variant.attrs.iter() )?; + + if !attrs.config.enabled.value( item_attrs.config.enabled.value( true ) ) + { + return Ok( qt!{} ) + } + + if fields.len() <= 0 + { + return Ok( qt!{} ) + } + + let ( args, use_src ) = if fields.len() == 1 + { + let field = fields.iter().next().unwrap(); + ( + qt!{ #field }, + qt!{ src }, + ) + } + else + { + let src_i = ( 0..fields.len() ).map( | e | + { + let i = syn::Index::from( e ); + qt!{ src.#i, } + }); + ( + qt!{ #fields }, + qt!{ #( #src_i )* }, + // qt!{ src.0, src.1 }, + ) + }; + + // qqq : make `debug` working for all branches + if attrs.config.debug.value( false ) + { + let debug = format! + ( + r#" +#[ automatically_derived ] +impl< {0} > {item_name}< {1} > +where + {2} +{{ + #[ inline ] + fn new( src : {args} ) -> Self + {{ + Self::{variant_name}( {use_src} ) + }} +}} + "#, + format!( "{}", qt!{ #generics_impl } ), + format!( "{}", qt!{ #generics_ty } ), + format!( "{}", qt!{ #generics_where } ), + ); + let about = format! + ( +r#"derive : New +item : {item_name} +field : {variant_name}"#, + ); + diag::report_print( about, original_input, debug ); + } + + Ok + ( + qt! + { + #[ automatically_derived ] + impl< #generics_impl > #item_name< #generics_ty > + where + #generics_where + { + #[ inline ] + fn new( src : #args ) -> Self + { + Self::#variant_name( #use_src ) + } + } + } + ) + +} diff --git a/module/core/derive_tools_meta/src/derive/variadic_from.rs b/module/core/derive_tools_meta/src/derive/variadic_from.rs index f719fe6799..9c917dc025 100644 --- a/module/core/derive_tools_meta/src/derive/variadic_from.rs +++ b/module/core/derive_tools_meta/src/derive/variadic_from.rs @@ -1,154 +1,160 @@ use super::*; -use macro_tools::{ type_struct, Result }; +use macro_tools::{ Result, format_ident, attr, diag }; use iter::{ IterExt, Itertools }; -// +/// This function generates an implementation of a variadic `From` trait for a given struct. +/// It handles both named and unnamed fields within the struct, generating appropriate code +/// for converting a tuple of fields into an instance of the struct. pub fn variadic_from( input : proc_macro::TokenStream ) -> Result< proc_macro2::TokenStream > { - let parsed = syn::parse::< type_struct::TypeStructParsed >( input )?; - let item_name = parsed.item_name; + let original_input = input.clone(); + let parsed = syn::parse::< syn::ItemStruct >( input )?; + let has_debug = attr::has_debug( parsed.attrs.iter() )?; + let item_name = &parsed.ident; + + let len = parsed.fields.len(); + let from_trait = format_ident!( "From{len}", ); + let from_method = format_ident!( "from{len}" ); + + let + ( + types, + fn_params, + src_into_vars, + vars + ) + : + ( Vec< _ >, Vec< _ >, Vec< _ >, Vec< _ > ) + = parsed.fields.iter().enumerate().map_result( | ( i, field ) | + { + let ident = field.ident.clone().map_or_else( || format_ident!( "_{i}" ), | e | e ); + let ty = field.ty.clone(); + Result::Ok + (( + qt!{ #ty, }, + qt!{ #ident : #ty, }, + qt!{ let #ident = ::core::convert::Into::into( #ident ); }, + qt!{ #ident, }, + )) + })? + .into_iter() + .multiunzip(); let result = match &parsed.fields { syn::Fields::Named( _ ) => { - let - ( - types, - fn_params, - src_into_vars, - vars - ) : ( Vec< _ >, Vec< _ >, Vec< _ >, Vec< _ > ) = parsed.fields.iter().map_result( | field | - { - let ident = field.ident.clone().ok_or_else( || syn_err!( parsed.item.span(), "Fields should be named" ) )?; - let ty = field.ty.clone(); - Result::Ok - (( - qt!{ #ty, }, - qt!{ #ident : #ty, }, - qt!{ let #ident = core::convert::Into::into( #ident ); }, - qt!{ #ident, }, - )) - })? - .into_iter().multiunzip(); - - let l = format!( "{}", parsed.fields.len() ); - let from_trait = macro_tools::format_ident!( "From_{l}" ); - let from_method = macro_tools::format_ident!( "from_{l}" ); - - qt! + if 1 <= len && len <= 3 { - - #[ automatically_derived ] - // impl wtools::From_2< i32 > for StructNamedFields - impl wtools::#from_trait< #( #types )* > for #item_name + qt! { - // fn from_1( a : i32, b : i32 ) -> Self - fn #from_method - ( - #( #fn_params )* - ) -> Self + + #[ automatically_derived ] + // impl variadic_from::From2< i32 > for StructNamedFields + impl variadic_from::#from_trait< #( #types )* > for #item_name { - #( #src_into_vars )* - // let a = core::convert::Into::into( a ); - // let b = core::convert::Into::into( b ); - Self + // fn from1( a : i32, b : i32 ) -> Self + fn #from_method + ( + #( #fn_params )* + ) -> Self { - #( #vars )* - // a, - // b, + #( #src_into_vars )* + // let a = ::core::convert::Into::into( a ); + // let b = ::core::convert::Into::into( b ); + Self + { + #( #vars )* + // a, + // b, + } } } - } - impl From< ( i32, i32 ) > for StructNamedFields - { - /// Returns the argument unchanged. - #[ inline( always ) ] - fn from( src : ( i32, i32 ) ) -> Self + impl From< ( #( #types )* ) > for #item_name { - Self::from_1( src ) + /// Reuse From1. + #[ inline( always ) ] + fn from( src : ( #( #types )* ) ) -> Self + { + Self::from1( src ) + } } - } + } + } + else + { + qt!{} } } syn::Fields::Unnamed( _ ) => { - let mut counter = 0; - let - ( - vars_assing_default, - src_into_vars, - vars - ) : ( Vec< _ >, Vec< _ >, Vec< _ > ) = parsed.fields.iter().map_result( | _field | - { - let ident = macro_tools::format_ident!( "_{}", format!( "{counter}" ) ); - counter += 1; - Result::Ok - (( - qt!{ let #ident = core::default::Default::default(); }, - qt!{ let #ident = src.into(); }, - qt!{ #ident, }, - )) - })? - .into_iter().multiunzip(); - - qt! + if 1 <= len && len <= 3 { - #[ automatically_derived ] - impl wtools::From_0 for #item_name + qt! { - fn from_0() -> Self + + #[ automatically_derived ] + // impl variadic_from::From2< i32 > for StructNamedFields + impl variadic_from::#from_trait< #( #types )* > for #item_name { - #( #vars_assing_default )* - // let a = Default::default(); - // let b = Default::default(); - // let c = Default::default(); - // let d = Default::default(); - Self + // fn from1( a : i32, b : i32 ) -> Self + fn #from_method ( - #( #vars )* - // a, - // b, - // c, - // d, - ) + #( #fn_params )* + ) -> Self + { + #( #src_into_vars )* + // let a = ::core::convert::Into::into( a ); + // let b = ::core::convert::Into::into( b ); + Self + ( + #( #vars )* + // a, + // b, + ) + } } - } - #[ automatically_derived ] - impl wtools::From_1< i32 > for #item_name - { - fn from_1( src : i32 ) -> Self + impl From< ( #( #types )* ) > for #item_name { - #( #src_into_vars )* - // let a = src.into(); - // let b = src.into(); - // let c = src.into(); - // let d = src.into(); - Self - ( - #( #vars )* - // a, - // b, - // c, - // d, - ) + /// Reuse From1. + #[ inline( always ) ] + fn from( src : ( #( #types )* ) ) -> Self + { + Self::from1( src ) + } } - } + } } + else + { + qt!{} + } + + } + syn::Fields::Unit => + { + + qt!{} } - _ => return Err( syn_err!( parsed.fields.span(), "Expects fields" ) ), + // _ => return Err( syn_err!( parsed.fields.span(), "Expects fields" ) ), }; + if has_debug + { + let about = format!( "derive : VariadicForm\nstructure : {item_name}" ); + diag::report_print( about, &original_input, &result ); + } + Ok( result ) } diff --git a/module/core/derive_tools_meta/src/lib.rs b/module/core/derive_tools_meta/src/lib.rs index 97cb37042f..1a034690bc 100644 --- a/module/core/derive_tools_meta/src/lib.rs +++ b/module/core/derive_tools_meta/src/lib.rs @@ -19,19 +19,19 @@ )] #[ cfg( feature = "enabled" ) ] mod derive; -#[ cfg -( - any - ( - feature = "derive_as_mut", - feature = "derive_as_ref", - feature = "derive_deref", - feature = "derive_deref_mut", - feature = "derive_from", - feature = "derive_inner_from", - feature = "derive_variadic_from", - ) -)] +// #[ cfg +// ( +// any +// ( +// feature = "derive_as_mut", +// feature = "derive_as_ref", +// feature = "derive_deref", +// feature = "derive_deref_mut", +// feature = "derive_from", +// feature = "derive_inner_from", +// feature = "derive_variadic_from", +// ) +// )] // #[ cfg( feature = "enabled" ) ] // use derive::*; @@ -72,7 +72,15 @@ mod derive; #[ cfg( feature = "enabled" ) ] #[ cfg( feature = "derive_from" ) ] -#[ proc_macro_derive( From ) ] +#[ proc_macro_derive +( + From, + attributes + ( + debug, // struct + from, // field + ) +)] pub fn from( input : proc_macro::TokenStream ) -> proc_macro::TokenStream { let result = derive::from::from( input ); @@ -84,33 +92,33 @@ pub fn from( input : proc_macro::TokenStream ) -> proc_macro::TokenStream } /// -/// Alias for derive `From`. Provides an automatic `From` implementation for struct wrapping a single value. +/// Provides an automatic `new` implementation for struct wrapping a single value. /// /// This macro simplifies the conversion of an inner type to an outer struct type /// when the outer type is a simple wrapper around the inner type. /// /// ## Example Usage /// -/// Instead of manually implementing `From< bool >` for `IsTransparent`: +/// Instead of manually implementing `new` for `IsTransparent`: /// /// ```rust /// pub struct IsTransparent( bool ); /// -/// impl From< bool > for IsTransparent +/// impl IsTransparent /// { /// #[ inline( always ) ] -/// fn from( src : bool ) -> Self +/// fn new( src : bool ) -> Self /// { /// Self( src ) /// } /// } /// ``` /// -/// Use `#[ derive( FromInner ) ]` to automatically generate the implementation: +/// Use `#[ derive( New ) ]` to automatically generate the implementation: /// /// ```rust /// # use derive_tools_meta::*; -/// #[ derive( FromInner ) ] +/// #[ derive( New ) ] /// pub struct IsTransparent( bool ); /// ``` /// @@ -118,11 +126,19 @@ pub fn from( input : proc_macro::TokenStream ) -> proc_macro::TokenStream /// #[ cfg( feature = "enabled" ) ] -#[ cfg( feature = "derive_from" ) ] -#[ proc_macro_derive( FromInner ) ] -pub fn from_inner( input : proc_macro::TokenStream ) -> proc_macro::TokenStream +#[ cfg( feature = "derive_new" ) ] +#[ proc_macro_derive +( + New, + attributes + ( + debug, // struct + new, // field + ) +)] +pub fn new( input : proc_macro::TokenStream ) -> proc_macro::TokenStream { - let result = derive::from::from( input ); + let result = derive::new::new( input ); match result { Ok( stream ) => stream.into(), @@ -130,6 +146,53 @@ pub fn from_inner( input : proc_macro::TokenStream ) -> proc_macro::TokenStream } } +// /// +// /// Alias for derive `From`. Provides an automatic `From` implementation for struct wrapping a single value. +// /// +// /// This macro simplifies the conversion of an inner type to an outer struct type +// /// when the outer type is a simple wrapper around the inner type. +// /// +// /// ## Example Usage +// /// +// /// Instead of manually implementing `From< bool >` for `IsTransparent`: +// /// +// /// ```rust +// /// pub struct IsTransparent( bool ); +// /// +// /// impl From< bool > for IsTransparent +// /// { +// /// #[ inline( always ) ] +// /// fn from( src : bool ) -> Self +// /// { +// /// Self( src ) +// /// } +// /// } +// /// ``` +// /// +// /// Use `#[ derive( FromInner ) ]` to automatically generate the implementation: +// /// +// /// ```rust +// /// # use derive_tools_meta::*; +// /// #[ derive( FromInner ) ] +// /// pub struct IsTransparent( bool ); +// /// ``` +// /// +// /// The macro facilitates the conversion without additional boilerplate code. +// /// +// +// #[ cfg( feature = "enabled" ) ] +// #[ cfg( feature = "derive_from" ) ] +// #[ proc_macro_derive( FromInner, attributes( debug ) ) ] +// pub fn from( input : proc_macro::TokenStream ) -> proc_macro::TokenStream +// { +// let result = derive::from::from( input ); +// match result +// { +// Ok( stream ) => stream.into(), +// Err( err ) => err.to_compile_error().into(), +// } +// } + /// /// Derive macro to implement From converting outer type into inner when-ever it's possible to do automatically. /// @@ -159,7 +222,7 @@ pub fn from_inner( input : proc_macro::TokenStream ) -> proc_macro::TokenStream #[ cfg( feature = "enabled" ) ] #[ cfg( feature = "derive_inner_from" ) ] -#[ proc_macro_derive( InnerFrom ) ] +#[ proc_macro_derive( InnerFrom, attributes( debug ) ) ] pub fn inner_from( input : proc_macro::TokenStream ) -> proc_macro::TokenStream { let result = derive::inner_from::inner_from( input ); @@ -200,7 +263,7 @@ pub fn inner_from( input : proc_macro::TokenStream ) -> proc_macro::TokenStream #[ cfg( feature = "enabled" ) ] #[ cfg( feature = "derive_deref" ) ] -#[ proc_macro_derive( Deref ) ] +#[ proc_macro_derive( Deref, attributes( debug ) ) ] pub fn deref( input : proc_macro::TokenStream ) -> proc_macro::TokenStream { let result = derive::deref::deref( input ); @@ -250,7 +313,7 @@ pub fn deref( input : proc_macro::TokenStream ) -> proc_macro::TokenStream #[ cfg( feature = "enabled" ) ] #[ cfg( feature = "derive_deref_mut" ) ] -#[ proc_macro_derive( DerefMut ) ] +#[ proc_macro_derive( DerefMut, attributes( debug ) ) ] pub fn deref_mut( input : proc_macro::TokenStream ) -> proc_macro::TokenStream { let result = derive::deref_mut::deref_mut( input ); @@ -289,7 +352,7 @@ pub fn deref_mut( input : proc_macro::TokenStream ) -> proc_macro::TokenStream #[ cfg( feature = "enabled" ) ] #[ cfg( feature = "derive_as_ref" ) ] -#[ proc_macro_derive( AsRef ) ] +#[ proc_macro_derive( AsRef, attributes( debug ) ) ] pub fn as_ref( input : proc_macro::TokenStream ) -> proc_macro::TokenStream { let result = derive::as_ref::as_ref( input ); @@ -329,7 +392,7 @@ pub fn as_ref( input : proc_macro::TokenStream ) -> proc_macro::TokenStream #[ cfg( feature = "enabled" ) ] #[ cfg( feature = "derive_as_mut" ) ] -#[ proc_macro_derive( AsMut ) ] +#[ proc_macro_derive( AsMut, attributes( debug ) ) ] pub fn as_mut( input : proc_macro::TokenStream ) -> proc_macro::TokenStream { let result = derive::as_mut::as_mut( input ); @@ -341,44 +404,100 @@ pub fn as_mut( input : proc_macro::TokenStream ) -> proc_macro::TokenStream } /// -/// Derive macro to implement default constructors `From_0`, `From_1`, `From_2`, `From_3`. +/// The `derive_variadic_from` macro is designed to provide a way to implement the `From`-like +/// traits for structs with a variable number of fields, allowing them to be constructed from +/// tuples of different lengths or from individual arguments. This functionality is particularly +/// useful for creating flexible constructors that enable different methods of instantiation for +/// a struct. By automating the implementation of traits, this macro reduces boilerplate code +/// and enhances code readability and maintainability. /// -/// ### Sample :: struct instead of macro. +/// ### Key Features /// -/// Write this +/// - **Flexible Construction**: Allows a struct to be constructed from different numbers of +/// arguments, converting each to the appropriate type. +/// - **Tuple Conversion**: Enables the struct to be constructed from tuples, leveraging the +/// `From` and `Into` traits for seamless conversion. +/// - **Code Generation**: Automates the implementation of these traits, reducing the need for +/// manual coding and ensuring consistent constructors. /// -/// ```rust, ignore, no_run -/// # use derive_tools::*; -/// #[ derive( Make ) ] -/// pub struct IsTransparent( bool ); -/// ``` +/// ### Limitations /// -/// Instead of this +/// Currently, the macro supports up to 3 arguments. If your struct has more than 3 fields, the +/// derive macro will generate no implementation. It supports tuple conversion, allowing structs +/// to be instantiated from tuples by leveraging the `From` and `Into` traits for seamless conversion. /// -/// ```rust, ignore, no_run -/// pub struct IsTransparent( bool ); -/// impl From_0 for IsTransparent +/// ### Example Usage +/// +/// This example demonstrates the use of the `variadic_from` macro to implement flexible +/// constructors for a struct, allowing it to be instantiated from different numbers of +/// arguments or tuples. It also showcases how to derive common traits like `Debug`, +/// `PartialEq`, `Default`, and `VariadicFrom` for the struct. +/// +/// ```rust +/// #[ cfg( not( all(feature = "enabled", feature = "type_variadic_from", feature = "derive_variadic_from" ) ) ) ] +/// fn main(){} +/// #[ cfg( all( feature = "enabled", feature = "type_variadic_from", feature = "derive_variadic_from" ) )] +/// fn main() /// { -/// fn make0() -> Self +/// use variadic_from::exposed::*; +/// +/// // Define a struct `MyStruct` with fields `a` and `b`. +/// // The struct derives common traits like `Debug`, `PartialEq`, `Default`, and `VariadicFrom`. +/// #[ derive( Debug, PartialEq, Default, VariadicFrom ) ] +/// // Use `#[ debug ]` to expand and debug generate code. +/// // #[ debug ] +/// struct MyStruct /// { -/// Self::default(); +/// a : i32, +/// b : i32, /// } -/// } -/// impl From_1 for IsTransparent -/// { -/// fn make1( src : bool ) -> Self +/// +/// // Implement the `From1` trait for `MyStruct`, which allows constructing a `MyStruct` instance +/// // from a single `i32` value by assigning it to both `a` and `b` fields. +/// impl From1< i32 > for MyStruct /// { -/// Self( src ) +/// fn from1( a : i32 ) -> Self { Self { a, b : a } } /// } +/// +/// let got : MyStruct = from!(); +/// let exp = MyStruct { a : 0, b : 0 }; +/// assert_eq!( got, exp ); +/// +/// let got : MyStruct = from!( 13 ); +/// let exp = MyStruct { a : 13, b : 13 }; +/// assert_eq!( got, exp ); +/// +/// let got : MyStruct = from!( 13, 14 ); +/// let exp = MyStruct { a : 13, b : 14 }; +/// assert_eq!( got, exp ); +/// +/// dbg!( exp ); +/// //> MyStruct { +/// //> a: 13, +/// //> b: 14, +/// //> } /// } +/// ``` /// +/// ### Debugging +/// +/// If your struct has a `debug` attribute, the macro will print information about the generated code for diagnostic purposes. +/// +/// ```rust, ignore +/// #[ derive( Debug, PartialEq, Default, VariadicFrom ) ] +/// // Use `#[ debug ]` to expand and debug generate code. +/// // #[ debug ] +/// struct MyStruct +/// { +/// a: i32, +/// b: i32, +/// } /// ``` - -// qqq : xxx : why no run/ignore? fix +/// #[ cfg( feature = "enabled" ) ] #[ cfg( feature = "derive_variadic_from" ) ] -#[ proc_macro_derive( VariadicFrom ) ] +#[ proc_macro_derive( VariadicFrom, attributes( debug ) ) ] pub fn derive_variadic_from( input : proc_macro::TokenStream ) -> proc_macro::TokenStream { let result = derive::variadic_from::variadic_from( input ); diff --git a/module/core/diagnostics_tools/Readme.md b/module/core/diagnostics_tools/Readme.md index 23e15200d7..d41d9b75a5 100644 --- a/module/core/diagnostics_tools/Readme.md +++ b/module/core/diagnostics_tools/Readme.md @@ -2,7 +2,7 @@ # Module :: diagnostics_tools - [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/module_diagnostics_tools_push.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/module_diagnostics_tools_push.yml) [![docs.rs](https://img.shields.io/docsrs/diagnostics_tools?color=e3e8f0&logo=docs.rs)](https://docs.rs/diagnostics_tools) [![Open in Gitpod](https://raster.shields.io/static/v1?label=try&message=online&color=eee&logo=gitpod&logoColor=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module/core/diagnostics_tools/examples/diagnostics_tools_trivial.rs,RUN_POSTFIX=--example%20/home/sakapoi/Документи/wTools_fork/module/core/diagnostics_tools/examples/diagnostics_tools_trivial/https://github.com/Wandalen/wTools) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) + [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/module_diagnostics_tools_push.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/module_diagnostics_tools_push.yml) [![docs.rs](https://img.shields.io/docsrs/diagnostics_tools?color=e3e8f0&logo=docs.rs)](https://docs.rs/diagnostics_tools) [![Open in Gitpod](https://raster.shields.io/static/v1?label=try&message=online&color=eee&logo=gitpod&logoColor=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fcore%2Fdiagnostics_tools%2Fexamples%2Fdiagnostics_tools_trivial.rs,RUN_POSTFIX=--example%20diagnostics_tools_trivial/https://github.com/Wandalen/wTools) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) Diagnostics tools. diff --git a/module/core/error_tools/Cargo.toml b/module/core/error_tools/Cargo.toml index e1398d3d64..1ef5cccee8 100644 --- a/module/core/error_tools/Cargo.toml +++ b/module/core/error_tools/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "error_tools" -version = "0.13.0" +version = "0.14.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", diff --git a/module/core/error_tools/Readme.md b/module/core/error_tools/Readme.md index ca1cc00933..cf2613a5ba 100644 --- a/module/core/error_tools/Readme.md +++ b/module/core/error_tools/Readme.md @@ -2,7 +2,7 @@ # Module :: error_tools - [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/module_error_tools_push.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/module_error_tools_push.yml) [![docs.rs](https://img.shields.io/docsrs/error_tools?color=e3e8f0&logo=docs.rs)](https://docs.rs/error_tools) [![Open in Gitpod](https://raster.shields.io/static/v1?label=try&message=online&color=eee&logo=gitpod&logoColor=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module/core/error_tools/examples/error_tools_trivial.rs,RUN_POSTFIX=--example%20/home/sakapoi/Документи/wTools_fork/module/core/error_tools/examples/error_tools_trivial/https://github.com/Wandalen/wTools) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) + [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/module_error_tools_push.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/module_error_tools_push.yml) [![docs.rs](https://img.shields.io/docsrs/error_tools?color=e3e8f0&logo=docs.rs)](https://docs.rs/error_tools) [![Open in Gitpod](https://raster.shields.io/static/v1?label=try&message=online&color=eee&logo=gitpod&logoColor=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fcore%2Ferror_tools%2Fexamples%2Ferror_tools_trivial.rs,RUN_POSTFIX=--example%20error_tools_trivial/https://github.com/Wandalen/wTools) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) Basic exceptions handling mechanism. diff --git a/module/core/for_each/Readme.md b/module/core/for_each/Readme.md index 694ab59968..eb0d2e3d5e 100644 --- a/module/core/for_each/Readme.md +++ b/module/core/for_each/Readme.md @@ -2,7 +2,7 @@ # Module :: for_each - [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/module_for_each_push.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/module_for_each_push.yml) [![docs.rs](https://img.shields.io/docsrs/for_each?color=e3e8f0&logo=docs.rs)](https://docs.rs/for_each) [![Open in Gitpod](https://raster.shields.io/static/v1?label=try&message=online&color=eee&logo=gitpod&logoColor=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module/core/for_each/examples/for_each_trivial.rs,RUN_POSTFIX=--example%20/home/sakapoi/Документи/wTools_fork/module/core/for_each/examples/for_each_trivial/https://github.com/Wandalen/wTools) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) + [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/module_for_each_push.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/module_for_each_push.yml) [![docs.rs](https://img.shields.io/docsrs/for_each?color=e3e8f0&logo=docs.rs)](https://docs.rs/for_each) [![Open in Gitpod](https://raster.shields.io/static/v1?label=try&message=online&color=eee&logo=gitpod&logoColor=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fcore%2Ffor_each%2Fexamples%2Ffor_each_trivial.rs,RUN_POSTFIX=--example%20for_each_trivial/https://github.com/Wandalen/wTools) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) Apply a macro for each element of a list. diff --git a/module/core/former/Cargo.toml b/module/core/former/Cargo.toml index 09489d9b38..563b0cc329 100644 --- a/module/core/former/Cargo.toml +++ b/module/core/former/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "former" -version = "1.0.0" +version = "2.1.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", @@ -26,8 +26,12 @@ all-features = false [features] -no_std = [ "collection_tools/no_std" ] -use_alloc = [ "no_std", "collection_tools/use_alloc" ] +no_std = [ "former_types/no_std", "collection_tools/no_std" ] +use_alloc = [ "no_std", "former_types/use_alloc", "collection_tools/use_alloc" ] + + +# no_std = [ "collection_tools/no_std" ] +# use_alloc = [ "no_std", "collection_tools/use_alloc" ] default = [ "enabled", @@ -47,11 +51,11 @@ full = [ "derive_components_assign", "derive_from_components", ] -enabled = [ "former_meta/enabled", "collection_tools/enabled" ] +enabled = [ "former_meta/enabled", "former_types/enabled" ] -derive_former = [ "former_meta/derive_former" ] -derive_components = [ "former_meta/derive_components" ] -derive_component_assign = [ "derive_components", "former_meta/derive_component_assign" ] +derive_former = [ "former_meta/derive_former", "former_types/derive_former" ] +derive_components = [ "former_meta/derive_components", "former_types/types_components" ] +derive_component_assign = [ "derive_components", "former_meta/derive_component_assign", "former_types/types_component_assign" ] derive_components_assign = [ "derive_components", "derive_component_assign", "former_meta/derive_components_assign" ] derive_component_from = [ "derive_components", "former_meta/derive_component_from" ] derive_from_components = [ "derive_components", "former_meta/derive_from_components" ] @@ -59,8 +63,10 @@ derive_from_components = [ "derive_components", "former_meta/derive_from_compone [dependencies] former_meta = { workspace = true } -collection_tools = { workspace = true, features = [ "collection_std", "collection_constructors" ] } +former_types = { workspace = true } +# collection_tools = { workspace = true, features = [ "collection_constructors" ] } [dev-dependencies] test_tools = { workspace = true, features = [ "full" ] } +collection_tools = { workspace = true, features = [ "collection_constructors" ] } diff --git a/module/core/former/Readme.md b/module/core/former/Readme.md index 35aea33b92..05852dba11 100644 --- a/module/core/former/Readme.md +++ b/module/core/former/Readme.md @@ -2,7 +2,7 @@ # Module :: former - [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/module_former_push.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/module_former_push.yml) [![docs.rs](https://img.shields.io/docsrs/former?color=e3e8f0&logo=docs.rs)](https://docs.rs/former) [![Open in Gitpod](https://raster.shields.io/static/v1?label=try&message=online&color=eee&logo=gitpod&logoColor=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module/core/former/examples/former_trivial.rs,RUN_POSTFIX=--example%20/home/sakapoi/Документи/wTools_fork/module/core/former/examples/former_trivial/https://github.com/Wandalen/wTools) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) + [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/module_former_push.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/module_former_push.yml) [![docs.rs](https://img.shields.io/docsrs/former?color=e3e8f0&logo=docs.rs)](https://docs.rs/former) [![Open in Gitpod](https://raster.shields.io/static/v1?label=try&message=online&color=eee&logo=gitpod&logoColor=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fcore%2Fformer%2Fexamples%2Fformer_trivial.rs,RUN_POSTFIX=--example%20former_trivial/https://github.com/Wandalen/wTools) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) A flexible and extensible implementation of the builder pattern. @@ -371,7 +371,7 @@ The provided code snippet illustrates a basic use-case of the Former, which is u 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.storage.age = ::core::option::Option::Some(::core::convert::Into::into( src )); self } @@ -380,7 +380,7 @@ The provided code snippet illustrates a basic use-case of the Former, which is u 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.storage.username = ::core::option::Option::Some(::core::convert::Into::into( src )); self } @@ -389,7 +389,7 @@ The provided code snippet illustrates a basic use-case of the Former, which is u 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.storage.bio_optional = ::core::option::Option::Some(::core::convert::Into::into( src )); self } } @@ -465,7 +465,7 @@ The provided code snippet illustrates a basic use-case of the Former, which is u Try out `cargo run --example former_trivial`. -
+
[See code](./examples/former_trivial.rs). ## Example : Custom and Alternative Setters @@ -518,7 +518,7 @@ 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. Try out `cargo run --example former_custom_setter`. -
+
[See code](./examples/former_custom_setter.rs). ## Example : Custom Setter Overriding @@ -533,33 +533,41 @@ But it's also possible to completely override setter and write its own from scra # fn main() # { -use former::Former; - -/// Structure with a custom setter. -#[ derive( Debug, Former ) ] -pub struct StructWithCustomSetters -{ - #[ scalar( setter = false ) ] - word : String, -} - -impl StructWithCustomSettersFormer -{ + use former::Former; - // Custom alternative setter for `word` - pub fn word( mut self, value : impl Into< String > ) -> Self + /// Structure with a custom setter. + #[ derive( Debug, Former ) ] + pub struct StructWithCustomSetters { - debug_assert!( self.storage.word.is_none() ); - self.storage.word = Some( format!( "{}!", value.into() ) ); - self + // Use `debug` to gennerate sketch of setter. + #[ scalar( setter = false ) ] + word : String, } -} + impl< Definition > StructWithCustomSettersFormer< Definition > + where + Definition : former::FormerDefinition< Storage = StructWithCustomSettersFormerStorage >, + { + // Custom alternative setter for `word` + #[ inline ] + pub fn word< Src >( mut self, src : Src ) -> Self + where + Src : ::core::convert::Into< String >, + { + debug_assert!( self.storage.word.is_none() ); + self.storage.word = Some( format!( "{}!", src.into() ) ); + self + } + } -let example = StructWithCustomSetters::former() -.word( "Hello" ) -.form(); -assert_eq!( example.word, "Hello!".to_string() ); + let example = StructWithCustomSetters::former() + .word( "Hello" ) + .form(); + assert_eq!( example.word, "Hello!".to_string() ); + dbg!( example ); + //> StructWithCustomSetters { + //> word: "Hello!", + //> } # } ``` @@ -567,7 +575,7 @@ 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. Try out `cargo run --example former_custom_setter_overriden`. -
+
[See code](./examples/former_custom_setter_overriden.rs). ## Example : Custom Defaults @@ -626,7 +634,7 @@ 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. Try out `cargo run --example former_custom_defaults`. -
+
[See code](./examples/former_custom_defaults.rs). ## Concept of Storage and Former @@ -638,7 +646,7 @@ 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`): +Storage is not just a passive collection; 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. @@ -646,142 +654,40 @@ Storage is not just a passive container; it is an active part of a larger ecosys 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: - -- `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. - -These traits collectively facilitate a robust and flexible builder pattern that supports complex object creation and configuration scenarios. - -## 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" ) ) ] -# 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 - -# } -``` - ## 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: +Understanding the distinctions among the types of setters or subformers is essential for effectively employing the builder pattern in object construction. Each type of setter is designed to meet specific needs in building complex, structured data entities: -- **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. +- **Scalar Setter**: Handles the direct assignment of scalar values or simple fields within an entity. These setters manage basic data types or individual fields and do not involve nested formers or complex 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 Collection Setter**: Facilitates the management of a collection as a whole by returning a former that provides an interface to configure the entire collection. This setter is beneficial for applying uniform configurations or validations to all elements in a 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`. +- **Subform Entry Setter**: This setter allows for the individual formation of elements within a collection. It returns a former for each element, enabling detailed configuration and addition of complex elements within collections, exemplified by managing `Child` entities within a `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. +- **Subform Scalar Setter**: Similar to the subform entry setter but designed for scalar fields that have a former implementation. This setter does not collect instances into a collection because there is no collection involved, only a scalar field. It is used when the scalar field itself needs to be configured or modified through its dedicated former. -## Example : Container Setter for a Vector +These setters ensure that developers can precisely and efficiently set properties, manage collections, and configure complex structures within their applications. -This example demonstrates how to employ the `Former` trait to configure a `Vec` using a container setter in a structured manner. +## Example : Collection Setter for a Vector + +This example demonstrates how to employ the `Former` trait to configure a `Vec` using a collection setter in a structured manner. ```rust -# #[ cfg( not( all( feature = "enabled", feature = "derive_former", not( feature = "no_std" ) ) ) ) ] +# #[ cfg( not( all( feature = "enabled", feature = "derive_former", any( feature = "use_alloc", not( feature = "no_std" ) ) ) ) ) ] # fn main() {} -# #[ cfg( all( feature = "enabled", feature = "derive_former", not( feature = "no_std" ) ) ) ] +# #[ cfg( all( feature = "enabled", feature = "derive_former", any( feature = "use_alloc", not( feature = "no_std" ) ) ) ) ] # fn main() # { #[ derive( Debug, PartialEq, former::Former ) ] pub struct StructWithVec { - #[ container ] + #[ subform_collection ] vec : Vec< &'static str >, } @@ -798,19 +704,19 @@ This example demonstrates how to employ the `Former` trait to configure a `Vec` # } ``` -Try out `cargo run --example former_container_vector`. -
-[See code](./examples/former_container_vector.rs). +Try out `cargo run --example former_collection_vector`. +
+[See code](./examples/former_collection_vector.rs). -## Example : Container Setter for a Hashmap +## Example : Collection Setter for a Hashmap -This example demonstrates how to effectively employ the `Former` trait to configure a `HashMap` using a container setter. +This example demonstrates how to effectively employ the `Former` trait to configure a `HashMap` using a collection setter. ```rust -# #[ cfg( not( all( feature = "enabled", feature = "derive_former", not( feature = "no_std" ) ) ) ) ] +# #[ cfg( not( all( feature = "enabled", feature = "derive_former", any( feature = "use_alloc", not( feature = "no_std" ) ) ) ) ) ] # fn main() {} -# #[ cfg( all( feature = "enabled", feature = "derive_former", not( feature = "no_std" ) ) ) ] +# #[ cfg( all( feature = "enabled", feature = "derive_former", any( feature = "use_alloc", not( feature = "no_std" ) ) ) ) ] # fn main() # { use collection_tools::{ HashMap, hmap }; @@ -818,7 +724,7 @@ This example demonstrates how to effectively employ the `Former` trait to config #[ derive( Debug, PartialEq, former::Former ) ] pub struct StructWithMap { - #[ container ] + #[ subform_collection ] map : HashMap< &'static str, &'static str >, } @@ -835,19 +741,19 @@ This example demonstrates how to effectively employ the `Former` trait to config # } ``` -Try out `cargo run --example former_container_hashmap`. -
-[See code](./examples/former_container_hashmap.rs). +Try out `cargo run --example former_collection_hashmap`. +
+[See code](./examples/former_collection_hashmap.rs). -## Example : Container Setter for a Hashset +## Example : Collection Setter for a Hashset This example demonstrates the use of the `Former` trait to build a `collection_tools::HashSet` through subforming. ```rust -# #[ cfg( not( all( feature = "enabled", feature = "derive_former", not( feature = "no_std" ) ) ) ) ] +# #[ cfg( not( all( feature = "enabled", feature = "derive_former", any( feature = "use_alloc", not( feature = "no_std" ) ) ) ) ) ] # fn main() {} -# #[ cfg( all( feature = "enabled", feature = "derive_former", not( feature = "no_std" ) ) ) ] +# #[ cfg( all( feature = "enabled", feature = "derive_former", any( feature = "use_alloc", not( feature = "no_std" ) ) ) ) ] # fn main() { use collection_tools::{ HashSet, hset }; @@ -855,7 +761,7 @@ This example demonstrates the use of the `Former` trait to build a `collection_t #[ derive( Debug, PartialEq, former::Former ) ] pub struct StructWithSet { - #[ container ] + #[ subform_collection ] set : HashSet< &'static str >, } @@ -872,21 +778,21 @@ This example demonstrates the use of the `Former` trait to build a `collection_t # } ``` -Try out `cargo run --example former_container_hashset`. -
-[See code](./examples/former_container_hashset.rs). +Try out `cargo run --example former_collection_hashset`. +
+[See code](./examples/former_collection_hashset.rs). ## Example : Custom Scalar Setter -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. +This example demonstrates the implementation of a scalar setter using the `Former` trait. Unlike the more complex subform and collection 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. +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 collection—each child entity in this case. ```rust -# #[ cfg( not( all( feature = "enabled", feature = "derive_former", not( feature = "no_std" ) ) ) ) ] +# #[ cfg( not( all( feature = "enabled", feature = "derive_former", any( feature = "use_alloc", not( feature = "no_std" ) ) ) ) ) ] # fn main() {} -# #[ cfg( all( feature = "enabled", feature = "derive_former", not( feature = "no_std" ) ) ) ] +# #[ cfg( all( feature = "enabled", feature = "derive_former", any( feature = "use_alloc", not( feature = "no_std" ) ) ) ) ] # fn main() # { use collection_tools::HashMap; @@ -906,8 +812,8 @@ The `child` function within `ParentFormer` is a custom subform setter that plays // #[ debug ] pub struct Parent { - // Use `hint = true` to gennerate sketch of setter. - #[ scalar( setter = false, hint = false ) ] + // Use `debug` to gennerate sketch of setter. + #[ scalar( setter = false ) ] children : HashMap< String, Child >, } @@ -952,28 +858,103 @@ The `child` function within `ParentFormer` is a custom subform setter that plays # } ``` -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`. +In this example, the `Parent` struct functions as a collection 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 +## Example : Custom Subform Scalar 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. +Implementation of a custom subform scalar setter using the `Former` trait in Rust. -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. +This example focuses on the usage of a subform scalar setter to manage complex scalar types within a parent structure. +Unlike more general subform setters that handle collections, this setter specifically configures scalar fields that have +their own formers, allowing for detailed configuration within a nested builder pattern. ```rust -# #[ cfg( not( all( feature = "enabled", feature = "derive_former", not( feature = "no_std" ) ) ) ) ] + +# #[ cfg( not( all( feature = "enabled", feature = "derive_former" ) ) ) ] +# fn main() +# {} +# +# // Ensures the example only compiles when the appropriate features are enabled. +# #[ cfg( all( feature = "enabled", feature = "derive_former" ) ) ] +# fn main() +# { + + use former::Former; + + // Child struct with Former derived for builder pattern support + #[ derive( Debug, PartialEq, Former ) ] + // Optional: Use `#[debug]` to expand and debug generated code. + // #[debug] + pub struct Child + { + name : String, + description : String, + } + + // Parent struct designed to hold a single Child instance using subform scalar + #[ derive( Debug, PartialEq, Former ) ] + // Optional: Use `#[debug]` to expand and debug generated code. + // #[debug] + pub struct Parent + { + // The `subform_scalar` attribute is used to specify that the 'child' field has its own former + // and can be individually configured via a subform setter. This is not a collection but a single scalar entity. + #[ subform_scalar( setter = false ) ] + child : Child, + } + + /// Extends `ParentFormer` to include a method that initializes and configures a subformer for the 'child' field. + /// This function demonstrates the dynamic addition of a named child, leveraging a subformer to specify detailed properties. + 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_subform_scalar::< ChildFormer< _ >, _, >().name( name ) + } + } + + // Creating an instance of `Parent` using the builder pattern to configure `Child` + let ca = Parent::former() + .child( "echo" ) // starts the configuration of the `child` subformer + .description( "prints all subjects and properties" ) // sets additional properties for the `Child` + .end() // finalize the child configuration + .form(); // finalize the Parent configuration + + dbg!( &ca ); // Outputs the structured data for review + // Expected output: + //> Parent { + //> child: Child { + //> name: "echo", + //> description: "prints all subjects and properties", + //> }, + //> } + +# } +``` + +## Example : Custom Subform Collection Setter + +This example demonstrates the use of collection setters to manage complex nested data structures with the `Former` trait, focusing on a parent-child relationship structured around a collection `HashMap`. Unlike typical builder patterns that add individual elements using subform setters, this example uses a collection 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 collection—each child entity in this case. + +```rust +# #[ cfg( not( all( feature = "enabled", feature = "derive_former", any( feature = "use_alloc", 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" ) ) ) ] +# #[ cfg( all( feature = "enabled", feature = "derive_former", any( feature = "use_alloc", not( feature = "no_std" ) ) ) ) ] # fn main() # { use collection_tools::HashMap; @@ -993,8 +974,8 @@ The `child` function within `ParentFormer` is a custom subform setter that plays // #[ debug ] pub struct Parent { - // Use `hint = true` to gennerate sketch of setter. - #[ scalar( setter = false, hint = false ) ] + // Use `debug` to gennerate sketch of setter. + #[ scalar( setter = false ) ] children : HashMap< String, Child >, } @@ -1039,22 +1020,22 @@ The `child` function within `ParentFormer` is a custom subform setter that plays # } ``` -Try out `cargo run --example former_custom_container_setter`. -
-[See code](./examples/former_custom_container_setter.rs). +Try out `cargo run --example former_custom_subform_collection`. +
+[See code](./examples/former_custom_subform_collection.rs). -## Example : Custom Subform Setter +## Example : Custom Subform Entry 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. +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 collection—each child entity in this case. ```rust -# #[ cfg( not( all( feature = "enabled", feature = "derive_former", not( feature = "no_std" ) ) ) ) ] +# #[ cfg( not( all( feature = "enabled", feature = "derive_former", any( feature = "use_alloc", 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" ) ) ) ] +# #[ cfg( all( feature = "enabled", feature = "derive_former", any( feature = "use_alloc", not( feature = "no_std" ) ) ) ) ] # fn main() # { use collection_tools::HashMap; @@ -1074,8 +1055,8 @@ The `child` function within `ParentFormer` is a custom subform setter that plays // #[ debug ] pub struct Parent { - // Use `hint = true` to gennerate sketch of setter. - #[ subform( setter = false, hint = false ) ] + // Use `debug` to gennerate sketch of setter. + #[ subform_entry( setter = false ) ] child : HashMap< String, Child >, } @@ -1091,7 +1072,7 @@ The `child` function within `ParentFormer` is a custom subform setter that plays #[ inline( always ) ] pub fn child( self, name : &str ) -> ChildAsSubformer< Self, impl ChildAsSubformerEnd< Self > > { - self._child_add::< ChildFormer< _ >, _, >() + self._child_subform_entry::< ChildFormer< _ >, _, >() .name( name ) } @@ -1133,27 +1114,27 @@ The `child` function within `ParentFormer` is a custom subform setter that plays # } ``` -Try out `cargo run --example former_custom_subform_setter`. -
-[See code](./examples/former_custom_subform_setter.rs). +Try out `cargo run --example former_custom_subform_entry`. +
+[See code](./examples/former_custom_subform_entry.rs). -## General Container Interface +## General Collection 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. +There are suite of traits designed to abstract and enhance the functionality of collection data structures within the forming process. These traits are integral to managing the complexity of collection operations, such as adding, modifying, and converting between different representations within collections like vectors, hash maps, etc. They are especially useful when used in conjunction with the `collection` 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. +- [`Collection`] - Defines basic functionalities for collections, managing entries and values, establishing the fundamental operations required for any custom collection implementation in forming processes. +- [`EntryToVal`] - Facilitates the conversion of collection entries to their value representations, crucial for operations that treat collection 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 collection based on value data. +- [`CollectionAdd`] - Adds functionality for inserting entries into a collection, considering collection-specific rules such as duplication handling and order preservation, enhancing the usability of collections in forming scenarios. +- [`CollectionAssign`] - Extends the collection functionality to replace all existing entries with new ones, enabling bulk updates or complete resets of collection contents, which is particularly useful in dynamic data environments. -## Custom Container Former +## Custom Collection 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. +Collection interface is defined in the crate and implemented for collections like vectors, hash maps, etc, but if you want to use non-standard collection you can implement collection interface for the collection. This example demonstrate how to do that. -Try out `cargo run --example former_custom_container`. -
-[See code](./examples/former_custom_container.rs). +Try out `cargo run --example former_custom_collection`. +
+[See code](./examples/former_custom_collection.rs). ## Concept of Mutator @@ -1214,7 +1195,7 @@ held within the storage. #[ derive( Debug, PartialEq, Former ) ] #[ storage_fields( a : i32, b : Option< String > ) ] - #[ mutator( custom = true ) ] + #[ mutator( custom ) ] pub struct Struct1 { c : String, @@ -1250,9 +1231,113 @@ held within the storage. ``` Try out `cargo run --example former_custom_mutator`. -
+
[See code](./examples/former_custom_mutator.rs). +## 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: + +- `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. + +These traits collectively facilitate a robust and flexible builder pattern that supports complex object creation and configuration scenarios. + +## Example : Custom Definition + +Define a custom former definition and custom forming logic, and apply them to a collection. + +The example showcases how to accumulate elements into a collection 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( not( all( feature = "enabled", feature = "derive_former", any( feature = "use_alloc", not( feature = "no_std" ) ) ) ) ) ] +# fn main() {} + +# #[ cfg( all( feature = "enabled", feature = "derive_former", any( feature = "use_alloc", not( feature = "no_std" ) ) ) ) ] +# 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; // Collection 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::CollectionFormer::::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 + +# } +``` + ## Index of Examples @@ -1262,7 +1347,7 @@ Try out `cargo run --example former_custom_mutator`. - [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. +- [Custom Definition](./examples/former_custom_definition.rs) - Define a custom former definition and custom forming logic, and apply them to a collection. diff --git a/module/core/former/examples/former_container_hashmap.rs b/module/core/former/examples/former_collection_hashmap.rs similarity index 67% rename from module/core/former/examples/former_container_hashmap.rs rename to module/core/former/examples/former_collection_hashmap.rs index 8908adc7ab..93b7ea3526 100644 --- a/module/core/former/examples/former_container_hashmap.rs +++ b/module/core/former/examples/former_collection_hashmap.rs @@ -1,10 +1,10 @@ //! -//! This example demonstrates how to effectively employ the `Former` trait to configure a `HashMap` using a container setter. +//! This example demonstrates how to effectively employ the `Former` trait to configure a `HashMap` using a collection setter. //! -#[ cfg( not( all( feature = "enabled", feature = "derive_former", not( feature = "no_std" ) ) ) ) ] +#[ cfg( not( all( feature = "enabled", feature = "derive_former", any( feature = "use_alloc", not( feature = "no_std" ) ) ) ) ) ] fn main() {} -#[ cfg( all( feature = "enabled", feature = "derive_former", not( feature = "no_std" ) ) ) ] +#[ cfg( all( feature = "enabled", feature = "derive_former", any( feature = "use_alloc", not( feature = "no_std" ) ) ) ) ] fn main() { use collection_tools::{ HashMap, hmap }; @@ -12,7 +12,7 @@ fn main() #[ derive( Debug, PartialEq, former::Former ) ] pub struct StructWithMap { - #[ container ] + #[ subform_collection ] map : HashMap< &'static str, &'static str >, } diff --git a/module/core/former/examples/former_container_hashset.rs b/module/core/former/examples/former_collection_hashset.rs similarity index 73% rename from module/core/former/examples/former_container_hashset.rs rename to module/core/former/examples/former_collection_hashset.rs index 2c76d24a0a..81c81f604f 100644 --- a/module/core/former/examples/former_container_hashset.rs +++ b/module/core/former/examples/former_collection_hashset.rs @@ -2,9 +2,9 @@ //! 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" ) ) ) ) ] +#[ cfg( not( all( feature = "enabled", feature = "derive_former", any( feature = "use_alloc", not( feature = "no_std" ) ) ) ) ) ] fn main() {} -#[ cfg( all( feature = "enabled", feature = "derive_former", not( feature = "no_std" ) ) ) ] +#[ cfg( all( feature = "enabled", feature = "derive_former", any( feature = "use_alloc", not( feature = "no_std" ) ) ) ) ] fn main() { use collection_tools::{ HashSet, hset }; @@ -12,7 +12,7 @@ fn main() #[ derive( Debug, PartialEq, former::Former ) ] pub struct StructWithSet { - #[ container ] + #[ subform_collection ] set : HashSet< &'static str >, } diff --git a/module/core/former/examples/former_container_vector.rs b/module/core/former/examples/former_collection_vector.rs similarity index 63% rename from module/core/former/examples/former_container_vector.rs rename to module/core/former/examples/former_collection_vector.rs index 92f67dbd47..b51b4fa378 100644 --- a/module/core/former/examples/former_container_vector.rs +++ b/module/core/former/examples/former_collection_vector.rs @@ -1,17 +1,17 @@ //! -//! This example demonstrates how to employ the `Former` trait to configure a `Vec` using a container setter in a structured manner. +//! This example demonstrates how to employ the `Former` trait to configure a `Vec` using a collection setter in a structured manner. //! -#[ cfg( not( all( feature = "enabled", feature = "derive_former", not( feature = "no_std" ) ) ) ) ] +#[ cfg( not( all( feature = "enabled", feature = "derive_former", any( feature = "use_alloc", not( feature = "no_std" ) ) ) ) ) ] fn main() {} -#[ cfg( all( feature = "enabled", feature = "derive_former", not( feature = "no_std" ) ) ) ] +#[ cfg( all( feature = "enabled", feature = "derive_former", any( feature = "use_alloc", not( feature = "no_std" ) ) ) ) ] fn main() { #[ derive( Debug, PartialEq, former::Former ) ] pub struct StructWithVec { - #[ container ] + #[ subform_collection ] vec : Vec< &'static str >, } diff --git a/module/core/former/examples/former_component_from.rs b/module/core/former/examples/former_component_from.rs index e7cadbb335..2472fdf7ef 100644 --- a/module/core/former/examples/former_component_from.rs +++ b/module/core/former/examples/former_component_from.rs @@ -14,10 +14,10 @@ //! - `debug` : Optional attribute to enable debug-level output during the macro expansion process. //! -#[ cfg( not( feature = "derive_component_from" ) ) ] +#[ cfg( not( all( feature = "enabled", feature = "derive_component_from" ) ) ) ] fn main() {} -#[ cfg( feature = "derive_component_from" ) ] +#[ cfg( all( feature = "enabled", feature = "derive_component_from" ) ) ] fn main() { diff --git a/module/core/former/examples/former_custom_container.rs b/module/core/former/examples/former_custom_collection.rs similarity index 80% rename from module/core/former/examples/former_custom_container.rs rename to module/core/former/examples/former_custom_collection.rs index c3e9ff0f8b..54062a7fc7 100644 --- a/module/core/former/examples/former_custom_container.rs +++ b/module/core/former/examples/former_custom_collection.rs @@ -1,18 +1,20 @@ -//! Example former_custom_container.rs +//! Example former_custom_collection.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 +//! This example demonstrates how to define and use a custom collection with former. +//! The custom collection implemented here is a `LoggingSet`, which extends the basic `HashSet` behavior +//! by logging each addition. This example illustrates how to integrate such custom collections with the //! Former trait system for use in structured data types. -#[ cfg( not( all( feature = "enabled", feature = "derive_former", not( feature = "no_std" ) ) ) ) ] +// qqq : replace !no_std with !no_std || use_alloc when collection_tools reexports iterators -- done +#[ cfg( not( all( feature = "enabled", feature = "derive_former", any( feature = "use_alloc", not( feature = "no_std" ) ) ) ) ) ] fn main() {} -#[ cfg( all( feature = "enabled", feature = "derive_former", not( feature = "no_std" ) ) ) ] + +#[ cfg( all( feature = "enabled", feature = "derive_former", any( feature = "use_alloc", not( feature = "no_std" ) ) ) ) ] fn main() { use collection_tools::HashSet; - // Custom container that logs additions. + // Custom collection that logs additions. #[ derive( Debug, PartialEq ) ] pub struct LoggingSet< K > where @@ -21,7 +23,7 @@ fn main() set : HashSet< K >, // Internal HashSet to store the elements. } - // Implement default for the custom container. + // Implement default for the custom collection. impl< K > Default for LoggingSet< K > where K : core::cmp::Eq + core::hash::Hash, @@ -36,13 +38,13 @@ fn main() } } - // Allow the custom container to be converted into an iterator, to iterate over the elements. + // Allow the custom collection 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 >; + type IntoIter = collection_tools::hset::IntoIter< K >; fn into_iter( self ) -> Self::IntoIter { @@ -56,7 +58,7 @@ fn main() K : std::cmp::Eq + std::hash::Hash, { type Item = &'a K; - type IntoIter = std::collections::hash_set::Iter< 'a, K >; + type IntoIter = collection_tools::hset::Iter< 'a, K >; fn into_iter( self ) -> Self::IntoIter { @@ -64,8 +66,8 @@ fn main() } } - // Implement the Container trait to integrate with the former system. - impl< K > former::Container for LoggingSet< K > + // Implement the Collection trait to integrate with the former system. + impl< K > former::Collection for LoggingSet< K > where K : core::cmp::Eq + core::hash::Hash, { @@ -79,8 +81,8 @@ fn main() } } - // Implement ContainerAdd to handle adding elements to the custom container. - impl< K > former::ContainerAdd for LoggingSet< K > + // Implement CollectionAdd to handle adding elements to the custom collection. + impl< K > former::CollectionAdd for LoggingSet< K > where K : core::cmp::Eq + core::hash::Hash, { @@ -91,8 +93,8 @@ fn main() } } - // Implement ContainerAssign to handle bulk assignment of elements. - impl< K > former::ContainerAssign for LoggingSet< K > + // Implement CollectionAssign to handle bulk assignment of elements. + impl< K > former::CollectionAssign for LoggingSet< K > where K : core::cmp::Eq + core::hash::Hash, { @@ -106,8 +108,8 @@ fn main() } } - // Implement ContainerValToEntry to convert values back to entries. - impl< K > former::ContainerValToEntry< K > for LoggingSet< K > + // Implement CollectionValToEntry to convert values back to entries. + impl< K > former::CollectionValToEntry< K > for LoggingSet< K > where K : core::cmp::Eq + core::hash::Hash, { @@ -121,7 +123,7 @@ fn main() // = storage - // Define storage behavior for the custom container. + // Define storage behavior for the custom collection. impl< K > former::Storage for LoggingSet< K > where @@ -138,19 +140,19 @@ fn main() { fn preform( self ) -> Self::Preformed { - self // Return the container as is. + self // Return the collection as is. } } // = definition types - // Definitions related to the type settings for the LoggingSet, which detail how the container should behave with former. + // Definitions related to the type settings for the LoggingSet, which detail how the collection 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. + _phantom : core::marker::PhantomData< ( K, Context, Formed ) >, } /// Specifies the storage, formed type, and context for the `LoggingSet` when used in a forming process. @@ -249,17 +251,17 @@ fn main() // = subformer - // Subformer type alias simplifies the usage of `ContainerFormer` with `LoggingSet`. + // Subformer type alias simplifies the usage of `CollectionFormer` with `LoggingSet`. pub type LoggingSetAsSubformer< K, Context, Formed, End > = - former::ContainerFormer::< K, LoggingSetDefinition< K, Context, Formed, End > >; + former::CollectionFormer::< K, LoggingSetDefinition< K, Context, Formed, End > >; - // == use custom container + // == use custom collection /// Parent required for the template. #[ derive( Debug, Default, PartialEq, former::Former ) ] pub struct Parent { - #[ container ] + #[ subform_collection ] children : LoggingSet< i32 >, } diff --git a/module/core/former/examples/former_custom_container_setter.rs b/module/core/former/examples/former_custom_container_setter.rs deleted file mode 100644 index d5565a0908..0000000000 --- a/module/core/former/examples/former_custom_container_setter.rs +++ /dev/null @@ -1,91 +0,0 @@ -// 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_definition.rs b/module/core/former/examples/former_custom_definition.rs index 40957cf3ce..df7203a188 100644 --- a/module/core/former/examples/former_custom_definition.rs +++ b/module/core/former/examples/former_custom_definition.rs @@ -1,15 +1,15 @@ //! ## Example : Custom Definition //! -//! Define a custom former definition and custom forming logic, and apply them to a container. +//! Define a custom former definition and custom forming logic, and apply them to a collection. //! -//! The example showcases how to accumulate elements into a container and then transform them into a single result +//! The example showcases how to accumulate elements into a collection 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" ) ) ) ] +#[ cfg( not( all( feature = "enabled", feature = "derive_former", any( feature = "use_alloc", not( feature = "no_std" ) ) ) ) ) ] fn main() {} -#[ cfg( all( feature = "derive_former", feature = "enabled" ) ) ] +#[ cfg( all( feature = "enabled", feature = "derive_former", any( feature = "use_alloc", not( feature = "no_std" ) ) ) ) ] fn main() { // Define a struct `Sum` that will act as a custom former definition. @@ -19,7 +19,7 @@ fn main() // This trait defines the types used during the forming process. impl former::FormerDefinitionTypes for Sum { - type Storage = Vec; // Container for the integers. + type Storage = Vec; // Collection 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. } @@ -59,7 +59,7 @@ fn main() } // Use the custom `Former` to sum a list of integers. - let got = former::ContainerFormer::::new(Sum) + let got = former::CollectionFormer::::new(Sum) .add( 1 ) // Add an integer to the storage. .add( 2 ) // Add another integer. .add( 10 ) // Add another integer. diff --git a/module/core/former/examples/former_custom_mutator.rs b/module/core/former/examples/former_custom_mutator.rs index 5d83fe77c9..e70323e588 100644 --- a/module/core/former/examples/former_custom_mutator.rs +++ b/module/core/former/examples/former_custom_mutator.rs @@ -41,7 +41,7 @@ fn main() #[ derive( Debug, PartialEq, Former ) ] #[ storage_fields( a : i32, b : Option< String > ) ] - #[ mutator( custom = true ) ] + #[ mutator( custom ) ] pub struct Struct1 { c : String, diff --git a/module/core/former/examples/former_custom_scalar_setter.rs b/module/core/former/examples/former_custom_scalar_setter.rs index e5acb1847b..753adcc618 100644 --- a/module/core/former/examples/former_custom_scalar_setter.rs +++ b/module/core/former/examples/former_custom_scalar_setter.rs @@ -4,26 +4,28 @@ //! //! 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. +//! Unlike the more complex subform and collection 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 collection—each child entity in this case. //! -//! #### Types of Setters +//! #### Types of Setters / Subformers //! -//! It's crucial to understand the differences among subform setters, container setters, and scalar setters: +//! Understanding the distinctions among the types of setters or subformers is essential for effectively employing the builder pattern in object construction. Each type of setter is designed to meet specific needs in building complex, structured data entities: //! -//! - **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. +//! - **Scalar Setter**: Handles the direct assignment of scalar values or simple fields within an entity. These setters manage basic data types or individual fields and do not involve nested formers or complex 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 Collection Setter**: Facilitates the management of a collection as a whole by returning a former that provides an interface to configure the entire collection. This setter is beneficial for applying uniform configurations or validations to all elements in a 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`. +//! - **Subform Entry Setter**: This setter allows for the individual formation of elements within a collection. It returns a former for each element, enabling detailed configuration and addition of complex elements within collections, exemplified by managing `Child` entities within a `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. +//! - **Subform Scalar Setter**: Similar to the subform entry setter but designed for scalar fields that have a former implementation. This setter does not collect instances into a collection because there is no collection involved, only a scalar field. It is used when the scalar field itself needs to be configured or modified through its dedicated former. +//! +//! These setters ensure that developers can precisely and efficiently set properties, manage collections, and configure complex structures within their applications. //! -#[ cfg( not( all( feature = "enabled", feature = "derive_former", not( feature = "no_std" ) ) ) ) ] +#[ cfg( not( all( feature = "enabled", feature = "derive_former", any( feature = "use_alloc", 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" ) ) ) ] +#[ cfg( all( feature = "enabled", feature = "derive_former", any( feature = "use_alloc", not( feature = "no_std" ) ) ) ) ] fn main() { use collection_tools::HashMap; @@ -45,8 +47,8 @@ fn main() // #[ debug ] pub struct Parent { - // Use `hint = true` to gennerate sketch of setter. - #[ scalar( setter = false, hint = false ) ] + // Use `debug` to gennerate sketch of setter. + #[ scalar( setter = false ) ] children : HashMap< String, Child >, } diff --git a/module/core/former/examples/former_custom_setter_overriden.rs b/module/core/former/examples/former_custom_setter_overriden.rs index 4723ab16e2..7c57e5eaa1 100644 --- a/module/core/former/examples/former_custom_setter_overriden.rs +++ b/module/core/former/examples/former_custom_setter_overriden.rs @@ -1,3 +1,6 @@ +//! +//! ## Example : Custom Setter Overriding +//! //! It's also possible to completely override setter and write its own from scratch. //! //! For that use attribe `[ setter( false ) ]` to disable setter. In the example, 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. @@ -15,25 +18,34 @@ fn main() #[ derive( Debug, Former ) ] pub struct StructWithCustomSetters { + // Use `debug` to gennerate sketch of setter. #[ scalar( setter = false ) ] word : String, } - impl StructWithCustomSettersFormer + impl< Definition > StructWithCustomSettersFormer< Definition > + where + Definition : former::FormerDefinition< Storage = StructWithCustomSettersFormerStorage >, { - // Custom alternative setter for `word` - pub fn word( mut self, value : impl Into< String > ) -> Self + #[ inline ] + pub fn word< Src >( mut self, src : Src ) -> Self + where + Src : ::core::convert::Into< String >, { debug_assert!( self.storage.word.is_none() ); - self.storage.word = Some( format!( "{}!", value.into() ) ); + self.storage.word = Some( format!( "{}!", src.into() ) ); self } - } let example = StructWithCustomSetters::former() .word( "Hello" ) .form(); assert_eq!( example.word, "Hello!".to_string() ); + dbg!( example ); + //> StructWithCustomSetters { + //> word: "Hello!", + //> } + } diff --git a/module/core/former/examples/former_custom_subform_collection.rs b/module/core/former/examples/former_custom_subform_collection.rs new file mode 100644 index 0000000000..f0de7350a0 --- /dev/null +++ b/module/core/former/examples/former_custom_subform_collection.rs @@ -0,0 +1,95 @@ +// Example former_custom_subform_collection.rs + +//! +//! ## Example : Custom Subform Collection Setter +//! +//! This example demonstrates the use of collection setters to manage complex nested data structures with the `Former` trait, focusing on a parent-child relationship structured around a collection `HashMap`. Unlike typical builder patterns that add individual elements using subform setters, this example uses a collection 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 collection—each child entity in this case. +//! +//! #### Types of Setters / Subformers +//! +//! Understanding the distinctions among the types of setters or subformers is essential for effectively employing the builder pattern in object construction. Each type of setter is designed to meet specific needs in building complex, structured data entities: +//! +//! - **Scalar Setter**: Handles the direct assignment of scalar values or simple fields within an entity. These setters manage basic data types or individual fields and do not involve nested formers or complex structuring. +//! +//! - **Subform Collection Setter**: Facilitates the management of a collection as a whole by returning a former that provides an interface to configure the entire collection. This setter is beneficial for applying uniform configurations or validations to all elements in a collection, such as a `HashMap` of children. +//! +//! - **Subform Entry Setter**: This setter allows for the individual formation of elements within a collection. It returns a former for each element, enabling detailed configuration and addition of complex elements within collections, exemplified by managing `Child` entities within a `Parent`'s `HashMap`. +//! +//! - **Subform Scalar Setter**: Similar to the subform entry setter but designed for scalar fields that have a former implementation. This setter does not collect instances into a collection because there is no collection involved, only a scalar field. It is used when the scalar field itself needs to be configured or modified through its dedicated former. +//! +//! These setters ensure that developers can precisely and efficiently set properties, manage collections, and configure complex structures within their applications. +//! + +// Ensure the example only compiles when the appropriate features are enabled. +#[ cfg( not( all( feature = "enabled", feature = "derive_former", any( feature = "use_alloc", not( feature = "no_std" ) ) ) ) ) ] +fn main() {} +#[ cfg( all( feature = "enabled", feature = "derive_former", any( feature = "use_alloc", 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 `debug` to gennerate sketch of setter. + #[ subform_collection( setter = false ) ] + children : HashMap< String, Child >, + } + + /// The containr setter provides a collection setter that returns a CollectionFormer tailored for managing a collection of child entities. It employs a generic collection 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::CollectionFormer:: + < + ( String, Child ), + former::HashMapDefinition< String, Child, Self, Self, ParentSubformCollectionChildrenEnd< Definition >, > + > + { + self._children_subform_collection() + } + + } + + 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_subform_setter.rs b/module/core/former/examples/former_custom_subform_entry.rs similarity index 58% rename from module/core/former/examples/former_custom_subform_setter.rs rename to module/core/former/examples/former_custom_subform_entry.rs index bff35ac6ea..e046434da8 100644 --- a/module/core/former/examples/former_custom_subform_setter.rs +++ b/module/core/former/examples/former_custom_subform_entry.rs @@ -1,28 +1,31 @@ -// Example former_custom_subformer.rs +// Example former_custom_subform_entry.rs +//! ## Example : Custom Subform Entry 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. +//! 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 collection—each child entity in this case. //! -//! #### Types of Setters +//! #### Types of Setters / Subformers //! -//! It's crucial to understand the differences among subform setters, container setters, and scalar setters: +//! Understanding the distinctions among the types of setters or subformers is essential for effectively employing the builder pattern in object construction. Each type of setter is designed to meet specific needs in building complex, structured data entities: //! -//! - **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. +//! - **Scalar Setter**: Handles the direct assignment of scalar values or simple fields within an entity. These setters manage basic data types or individual fields and do not involve nested formers or complex 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 Collection Setter**: Facilitates the management of a collection as a whole by returning a former that provides an interface to configure the entire collection. This setter is beneficial for applying uniform configurations or validations to all elements in a 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`. +//! - **Subform Entry Setter**: This setter allows for the individual formation of elements within a collection. It returns a former for each element, enabling detailed configuration and addition of complex elements within collections, exemplified by managing `Child` entities within a `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. +//! - **Subform Scalar Setter**: Similar to the subform entry setter but designed for scalar fields that have a former implementation. This setter does not collect instances into a collection because there is no collection involved, only a scalar field. It is used when the scalar field itself needs to be configured or modified through its dedicated former. +//! +//! These setters ensure that developers can precisely and efficiently set properties, manage collections, and configure complex structures within their applications. //! -#[ cfg( not( all( feature = "enabled", feature = "derive_former", not( feature = "no_std" ) ) ) ) ] +#[ cfg( not( all( feature = "enabled", feature = "derive_former", any( feature = "use_alloc", 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" ) ) ) ] +#[ cfg( all( feature = "enabled", feature = "derive_former", any( feature = "use_alloc", not( feature = "no_std" ) ) ) ) ] fn main() { use collection_tools::HashMap; @@ -44,8 +47,8 @@ fn main() // #[ debug ] pub struct Parent { - // Use `hint = true` to gennerate sketch of setter. - #[ subform( setter = false, hint = false ) ] + // Use `debug` to gennerate sketch of setter. + #[ subform_entry( setter = false ) ] child : HashMap< String, Child >, } @@ -61,7 +64,7 @@ fn main() #[ inline( always ) ] pub fn child( self, name : &str ) -> ChildAsSubformer< Self, impl ChildAsSubformerEnd< Self > > { - self._child_add::< ChildFormer< _ >, _, >() + self._child_subform_entry::< ChildFormer< _ >, _, >() .name( name ) } diff --git a/module/core/former/examples/former_custom_subform_setter2.rs b/module/core/former/examples/former_custom_subform_entry2.rs similarity index 68% rename from module/core/former/examples/former_custom_subform_setter2.rs rename to module/core/former/examples/former_custom_subform_entry2.rs index 7c781d6128..c4592ee34f 100644 --- a/module/core/former/examples/former_custom_subform_setter2.rs +++ b/module/core/former/examples/former_custom_subform_entry2.rs @@ -1,29 +1,31 @@ // 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`. +//! 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_subform_entry`), 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. +//! 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 collection—each child entity in this case. //! -//! #### Types of Setters +//! #### Types of Setters / Subformers //! -//! It's crucial to understand the differences among subform setters, container setters, and scalar setters: +//! Understanding the distinctions among the types of setters or subformers is essential for effectively employing the builder pattern in object construction. Each type of setter is designed to meet specific needs in building complex, structured data entities: //! -//! - **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. +//! - **Scalar Setter**: Handles the direct assignment of scalar values or simple fields within an entity. These setters manage basic data types or individual fields and do not involve nested formers or complex 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 Collection Setter**: Facilitates the management of a collection as a whole by returning a former that provides an interface to configure the entire collection. This setter is beneficial for applying uniform configurations or validations to all elements in a 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`. +//! - **Subform Entry Setter**: This setter allows for the individual formation of elements within a collection. It returns a former for each element, enabling detailed configuration and addition of complex elements within collections, exemplified by managing `Child` entities within a `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. +//! - **Subform Scalar Setter**: Similar to the subform entry setter but designed for scalar fields that have a former implementation. This setter does not collect instances into a collection because there is no collection involved, only a scalar field. It is used when the scalar field itself needs to be configured or modified through its dedicated former. +//! +//! These setters ensure that developers can precisely and efficiently set properties, manage collections, and configure complex structures within their applications. //! // Ensure the example only compiles when the appropriate features are enabled. -#[ cfg( not( all( feature = "enabled", feature = "derive_former", not( feature = "no_std" ) ) ) ) ] +#[ cfg( not( all( feature = "enabled", feature = "derive_former", any( feature = "use_alloc", not( feature = "no_std" ) ) ) ) ) ] fn main() {} -#[ cfg( all( feature = "enabled", feature = "derive_former", not( feature = "no_std" ) ) ) ] +#[ cfg( all( feature = "enabled", feature = "derive_former", any( feature = "use_alloc", not( feature = "no_std" ) ) ) ) ] fn main() { use collection_tools::HashMap; @@ -45,8 +47,8 @@ fn main() // #[ debug ] pub struct Parent { - // Use `hint = true` to gennerate sketch of setter. - #[ subform( setter = false, hint = false ) ] + // Use `debug` to gennerate sketch of setter. + #[ subform_entry( setter = false ) ] child : HashMap< String, Child >, } @@ -73,7 +75,7 @@ fn main() super_former.storage.child = Some( Default::default() ); } - // add instance to the container + // add instance to the collection super_former.storage.child.as_mut().unwrap() .entry( preformed.name.clone() ) .or_insert( preformed.clone() ); @@ -85,7 +87,7 @@ fn main() } /// 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 + /// Unlike traditional methods that might use predefined setters like `_child_subform_entry`, this function /// explicitly constructs a subformer setup through a closure to provide greater flexibility and control. /// #[ inline( always ) ] @@ -101,12 +103,12 @@ fn main() super_former.storage.child = Some( Default::default() ); } - // add instance to the container + // add instance to the collection super_former.storage.child.as_mut().unwrap() .entry( preformed.name.clone() ) .or_insert( preformed.clone() ); - // custom logic to add two instances to the container + // custom logic to add two instances to the collection super_former.storage.child.as_mut().unwrap() .entry( format!( "{}_2", preformed.name ) ) .or_insert( preformed.clone() ); diff --git a/module/core/former/examples/former_custom_subform_scalar.rs b/module/core/former/examples/former_custom_subform_scalar.rs new file mode 100644 index 0000000000..7bb0eb97cd --- /dev/null +++ b/module/core/former/examples/former_custom_subform_scalar.rs @@ -0,0 +1,88 @@ +// Example former_custom_subform_scalar.rs + +//! +//! ## Example : Custom Subform Scalar Setter +//! +//! Implementation of a custom subform scalar setter using the `Former` trait in Rust. +//! +//! This example focuses on the usage of a subform scalar setter to manage complex scalar types within a parent structure. +//! Unlike more general subform setters that handle collections, this setter specifically configures scalar fields that have +//! their own formers, allowing for detailed configuration within a nested builder pattern. +//! +//! #### Types of Setters / Subformers +//! +//! Understanding the distinctions among the types of setters or subformers is essential for effectively employing the builder pattern in object construction. Each type of setter is designed to meet specific needs in building complex, structured data entities: +//! +//! - **Scalar Setter**: Handles the direct assignment of scalar values or simple fields within an entity. These setters manage basic data types or individual fields and do not involve nested formers or complex structuring. +//! +//! - **Subform Collection Setter**: Facilitates the management of a collection as a whole by returning a former that provides an interface to configure the entire collection. This setter is beneficial for applying uniform configurations or validations to all elements in a collection, such as a `HashMap` of children. +//! +//! - **Subform Entry Setter**: This setter allows for the individual formation of elements within a collection. It returns a former for each element, enabling detailed configuration and addition of complex elements within collections, exemplified by managing `Child` entities within a `Parent`'s `HashMap`. +//! +//! - **Subform Scalar Setter**: Similar to the subform entry setter but designed for scalar fields that have a former implementation. This setter does not collect instances into a collection because there is no collection involved, only a scalar field. It is used when the scalar field itself needs to be configured or modified through its dedicated former. +//! +//! These setters ensure that developers can precisely and efficiently set properties, manage collections, and configure complex structures within their applications. +//! + + +#[ cfg( not( all( feature = "enabled", feature = "derive_former", any( feature = "use_alloc",not( feature = "no_std" ) ) ) ) ) ] +fn main() +{} + +// Ensures the example only compiles when the appropriate features are enabled. +#[ cfg( all( feature = "enabled", feature = "derive_former", any( feature = "use_alloc",not( feature = "no_std" ) ) ) ) ] +fn main() +{ + use former::Former; + + // Child struct with Former derived for builder pattern support + #[ derive( Debug, PartialEq, Former ) ] + // Optional: Use `#[debug]` to expand and debug generated code. + // #[debug] + pub struct Child + { + name : String, + description : String, + } + + // Parent struct designed to hold a single Child instance using subform scalar + #[ derive( Debug, PartialEq, Former ) ] + // Optional: Use `#[debug]` to expand and debug generated code. + // #[debug] + pub struct Parent + { + // The `subform_scalar` attribute is used to specify that the 'child' field has its own former + // and can be individually configured via a subform setter. This is not a collection but a single scalar entity. + #[ subform_scalar( setter = false ) ] + child : Child, + } + + /// Extends `ParentFormer` to include a method that initializes and configures a subformer for the 'child' field. + /// This function demonstrates the dynamic addition of a named child, leveraging a subformer to specify detailed properties. + 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_subform_scalar::< ChildFormer< _ >, _, >().name( name ) + } + } + + // Creating an instance of `Parent` using the builder pattern to configure `Child` + let ca = Parent::former() + .child( "echo" ) // starts the configuration of the `child` subformer + .description( "prints all subjects and properties" ) // sets additional properties for the `Child` + .end() // finalize the child configuration + .form(); // finalize the Parent configuration + + dbg!( &ca ); // Outputs the structured data for review + // Expected output: + //> Parent { + //> child: Child { + //> name: "echo", + //> description: "prints all subjects and properties", + //> }, + //> } +} diff --git a/module/core/former/examples/former_many_fields.rs b/module/core/former/examples/former_many_fields.rs index 2e04953c77..1ca64722e0 100644 --- a/module/core/former/examples/former_many_fields.rs +++ b/module/core/former/examples/former_many_fields.rs @@ -17,10 +17,10 @@ //! //! 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" ) ) ) ] +#[ cfg( not( all( feature = "enabled", feature = "derive_former", any( feature = "use_alloc", not( feature = "no_std" ) ) ) ) ) ] fn main() {} -#[ cfg( all( feature = "derive_former", feature = "enabled" ) ) ] +#[ cfg( all( feature = "enabled", feature = "derive_former", any( feature = "use_alloc", not( feature = "no_std" ) ) ) ) ] fn main() { use former::Former; diff --git a/module/core/former/examples/former_trivial_expaned.rs b/module/core/former/examples/former_trivial_expaned.rs index 484f19262b..4968b11838 100644 --- a/module/core/former/examples/former_trivial_expaned.rs +++ b/module/core/former/examples/former_trivial_expaned.rs @@ -317,7 +317,7 @@ fn main() 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.storage.age = ::core::option::Option::Some(::core::convert::Into::into( src )); self } @@ -326,7 +326,7 @@ fn main() 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.storage.username = ::core::option::Option::Some(::core::convert::Into::into( src )); self } @@ -335,7 +335,7 @@ fn main() 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.storage.bio_optional = ::core::option::Option::Some(::core::convert::Into::into( src )); self } } diff --git a/module/core/former/src/component.rs b/module/core/former/src/component.rs deleted file mode 100644 index 67ea2fdabb..0000000000 --- a/module/core/former/src/component.rs +++ /dev/null @@ -1,115 +0,0 @@ -/// Provides a generic interface for setting a component of a certain type on an object. -/// -/// This trait abstracts the action of setting or replacing a component, where a component -/// can be any part or attribute of an object, such as a field value. It is designed to be -/// generic over the type of the component being set ( `T` ) and the type that can be converted -/// into the component ( `IntoT` ). This design allows for flexible implementations that can -/// accept various types that can then be converted into the required component type. -/// -/// # Type Parameters -/// -/// - `T` : The type of the component to be set on the implementing object. This type represents -/// the final form of the component as it should be stored or represented in the object. -/// - `IntoT` : The type that can be converted into `T`. This allows the `set` method to accept -/// different types that are capable of being transformed into the required component type `T`, -/// providing greater flexibility in setting the component. -/// -/// # Examples -/// -/// Implementing `ComponentAssign` to set a name string on a struct : -/// -/// ```rust -/// use former::ComponentAssign; -/// -/// struct MyStruct -/// { -/// name : String, -/// } -/// -/// impl< IntoT : Into< String > > ComponentAssign< String, IntoT > for MyStruct -/// { -/// fn assign( &mut self, component : IntoT ) -/// { -/// self.name = component.into(); -/// } -/// } -/// -/// let mut obj = MyStruct { name : String::new() }; -/// obj.assign( "New Name" ); -/// assert_eq!( obj.name, "New Name" ); -/// ``` -#[ cfg( any( feature = "derive_component_assign", feature = "derive_components_assign" ) ) ] -pub trait ComponentAssign< T, IntoT > -where - IntoT : Into< T >, -{ - /// Sets or replaces the component on the object with the given value. - /// - /// This method takes ownership of the given value ( `component` ), which is of type `IntoT`. - /// `component` is then converted into type `T` and set as the component of the object. - fn assign( &mut self, component : IntoT ); -} - -/// The `AssignWithType` trait provides a mechanism to set a component on an object, utilizing the type information explicitly. This trait extends the functionality of `SetComponen`t by allowing implementers to specify the component's type at the method call site, enhancing expressiveness in code that manipulates object states. -/// -/// ### Method Detail -/// -/// - `assign_with_type::< T, IntoT >( &mut self, component : IntoT )` -/// -/// This method allows an implementer of `SetWithTyp`e to set a component on self where the component's type is T, and the input value is of type `IntoT`, which can be converted into `T`. This method bridges the gap between dynamic type usage and static type enforcement, providing a flexible yet type-safe interface for modifying object states. -/// -/// ### Type Parameters -/// -/// - `T` : The type of the component to be set on the implementing object. This specifies the exact type expected by the object as its component. -/// - `IntoT` : A type that can be converted into T, providing flexibility in the types of values that can be used to set the component. -/// -/// ### Example -/// -/// ```rust -/// use former::{ ComponentAssign, AssignWithType }; -/// -/// struct UserProfile -/// { -/// username : String, -/// } -/// -/// impl< IntoT : Into< String > > ComponentAssign< String, IntoT > for UserProfile -// where String: From< String >, -/// { -/// fn assign( &mut self, component : IntoT ) -/// { -/// self.username = component.into(); -/// } -/// } -/// -/// let mut user_profile = UserProfile { username : String::new() }; -/// user_profile.assign_with_type::< String, _ >( "john_doe" ); -/// -/// assert_eq!( user_profile.username, "john_doe" ); -/// ``` -/// - -#[ cfg( any( feature = "derive_component_assign", feature = "derive_components_assign" ) ) ] -pub trait AssignWithType -{ - /// Function to set value of a component by its type. - fn assign_with_type< T, IntoT >( &mut self, component : IntoT ) - where - IntoT : Into< T >, - Self : ComponentAssign< T, IntoT >; -} - -#[ cfg( any( feature = "derive_component_assign", feature = "derive_components_assign" ) ) ] -impl< S > AssignWithType for S -{ - - #[ inline( always ) ] - fn assign_with_type< T, IntoT >( &mut self, component : IntoT ) - where - IntoT : Into< T >, - Self : ComponentAssign< T, IntoT >, - { - ComponentAssign::< T, IntoT >::assign( self, component ); - } - -} diff --git a/module/core/former/src/lib.rs b/module/core/former/src/lib.rs index 9653b8152e..2eb4e674d2 100644 --- a/module/core/former/src/lib.rs +++ b/module/core/former/src/lib.rs @@ -4,38 +4,39 @@ #![ doc( html_root_url = "https://docs.rs/former/latest/former/" ) ] #![ doc = include_str!( concat!( env!( "CARGO_MANIFEST_DIR" ), "/", "Readme.md" ) ) ] -/// Axiomatic things. -#[ cfg( feature = "enabled" ) ] -#[ cfg( feature = "derive_former" ) ] -mod axiomatic; -/// Forming process. -#[ cfg( feature = "enabled" ) ] -#[ cfg( feature = "derive_former" ) ] -mod definition; -/// Forming process. -#[ cfg( feature = "enabled" ) ] -#[ cfg( feature = "derive_former" ) ] -mod forming; -/// Storage. -#[ cfg( feature = "enabled" ) ] -#[ cfg( feature = "derive_former" ) ] -mod storage; - -/// Interface for containers. -#[ cfg( feature = "enabled" ) ] -#[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] -#[ cfg( feature = "derive_former" ) ] -mod container; - -/// Component-based forming. -#[ cfg( feature = "enabled" ) ] -#[ cfg( any( feature = "derive_component_from", feature = "derive_component_assign" ) ) ] -mod component; +// /// Axiomatic things. +// #[ cfg( feature = "enabled" ) ] +// #[ cfg( feature = "derive_former" ) ] +// mod axiomatic; +// /// Forming process. +// #[ cfg( feature = "enabled" ) ] +// #[ cfg( feature = "derive_former" ) ] +// mod definition; +// /// Forming process. +// #[ cfg( feature = "enabled" ) ] +// #[ cfg( feature = "derive_former" ) ] +// mod forming; +// /// Storage. +// #[ cfg( feature = "enabled" ) ] +// #[ cfg( feature = "derive_former" ) ] +// mod storage; +// +// /// Interface for collections. +// #[ cfg( feature = "enabled" ) ] +// #[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] +// #[ cfg( feature = "derive_former" ) ] +// mod collection; +// +// /// Component-based forming. +// #[ cfg( feature = "enabled" ) ] +// #[ cfg( any( feature = "derive_component_from", feature = "derive_component_assign" ) ) ] +// mod component; /// Namespace with dependencies. #[ cfg( feature = "enabled" ) ] pub mod dependency { + pub use former_types; pub use former_meta; } @@ -80,22 +81,24 @@ pub mod exposed #[ doc( inline ) ] #[ allow( unused_imports ) ] - #[ cfg( feature = "enabled" ) ] - #[ cfg( feature = "derive_former" ) ] - pub use super:: - { - axiomatic::*, - definition::*, - forming::*, - storage::*, - }; + pub use former_types::exposed::*; - #[ doc( inline ) ] - #[ allow( unused_imports ) ] - #[ cfg( feature = "enabled" ) ] - #[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] - #[ cfg( feature = "derive_former" ) ] - pub use super::container::*; +// #[ doc( inline ) ] +// #[ allow( unused_imports ) ] +// #[ cfg( feature = "derive_former" ) ] +// pub use super:: +// { +// axiomatic::*, +// definition::*, +// forming::*, +// storage::*, +// }; +// +// #[ doc( inline ) ] +// #[ allow( unused_imports ) ] +// #[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] +// #[ cfg( feature = "derive_former" ) ] +// pub use super::collection::*; } @@ -103,9 +106,14 @@ pub mod exposed #[ cfg( feature = "enabled" ) ] pub mod prelude { + + // #[ doc( inline ) ] + // #[ allow( unused_imports ) ] + // #[ cfg( any( feature = "derive_component_from", feature = "derive_component_assign" ) ) ] + // pub use super::component::*; + #[ doc( inline ) ] #[ allow( unused_imports ) ] - #[ cfg( feature = "enabled" ) ] - #[ cfg( any( feature = "derive_component_from", feature = "derive_component_assign" ) ) ] - pub use super::component::*; + pub use former_types::prelude::*; + } diff --git a/module/core/former/tests/inc/components_tests/component_assign.rs b/module/core/former/tests/inc/components_tests/component_assign.rs index 9ca13cbcba..546cb3852b 100644 --- a/module/core/former/tests/inc/components_tests/component_assign.rs +++ b/module/core/former/tests/inc/components_tests/component_assign.rs @@ -1,10 +1,10 @@ #[ allow( unused_imports ) ] use super::*; #[ allow( unused_imports ) ] -use former::ComponentAssign; +use former::Assign; -#[ derive( Default, PartialEq, Debug, former::ComponentAssign ) ] +#[ derive( Default, PartialEq, Debug, former::Assign ) ] // #[ debug ] struct Person { @@ -14,4 +14,4 @@ struct Person // -include!( "./only_test/components_component_assign.rs" ); +include!( "./only_test/component_assign.rs" ); diff --git a/module/core/former/tests/inc/components_tests/component_assign_manual.rs b/module/core/former/tests/inc/components_tests/component_assign_manual.rs index d2aff86d2c..fe1131845a 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 @@ -1,7 +1,7 @@ #[ allow( unused_imports ) ] use super::*; #[ allow( unused_imports ) ] -use former::ComponentAssign; +use former::Assign; #[ derive( Default, PartialEq, Debug ) ] @@ -11,7 +11,7 @@ struct Person name : String, } -impl< IntoT > ComponentAssign< i32, IntoT > for Person +impl< IntoT > Assign< i32, IntoT > for Person where IntoT : Into< i32 >, { @@ -21,7 +21,7 @@ where } } -impl< IntoT > ComponentAssign< String, IntoT > for Person +impl< IntoT > Assign< String, IntoT > for Person where IntoT : Into< String >, { @@ -33,4 +33,4 @@ where // -include!( "./only_test/components_component_assign.rs" ); +include!( "./only_test/component_assign.rs" ); diff --git a/module/core/former/tests/inc/components_tests/component_from.rs b/module/core/former/tests/inc/components_tests/component_from.rs index 965218114f..2151d3d3d7 100644 --- a/module/core/former/tests/inc/components_tests/component_from.rs +++ b/module/core/former/tests/inc/components_tests/component_from.rs @@ -17,4 +17,4 @@ pub struct Options1 // -include!( "./only_test/components_component_from.rs" ); +include!( "./only_test/component_from.rs" ); diff --git a/module/core/former/tests/inc/components_tests/component_from_manual.rs b/module/core/former/tests/inc/components_tests/component_from_manual.rs index 215a1f6ff5..94e854b381 100644 --- a/module/core/former/tests/inc/components_tests/component_from_manual.rs +++ b/module/core/former/tests/inc/components_tests/component_from_manual.rs @@ -42,4 +42,4 @@ impl From< &Options1 > for f32 // -include!( "./only_test/components_component_from.rs" ); +include!( "./only_test/component_from.rs" ); diff --git a/module/core/former/tests/inc/components_tests/components_assign.rs b/module/core/former/tests/inc/components_tests/components_assign.rs index e2dbb6fda4..2867a3cc8b 100644 --- a/module/core/former/tests/inc/components_tests/components_assign.rs +++ b/module/core/former/tests/inc/components_tests/components_assign.rs @@ -1,13 +1,13 @@ #[ allow( unused_imports ) ] use super::*; #[ allow( unused_imports ) ] -use former::{ ComponentAssign, AssignWithType }; +use former::{ Assign, AssignWithType }; /// /// Options1 /// -#[ derive( Debug, Default, PartialEq, the_module::ComponentAssign, the_module::ComponentsAssign ) ] +#[ derive( Debug, Default, PartialEq, the_module::Assign, the_module::ComponentsAssign ) ] pub struct Options1 { field1 : i32, @@ -46,7 +46,7 @@ impl From< &Options1 > for f32 /// Options2 /// -#[ derive( Debug, Default, PartialEq, the_module::ComponentAssign, the_module::ComponentsAssign ) ] +#[ derive( Debug, Default, PartialEq, the_module::Assign, the_module::ComponentsAssign ) ] pub struct Options2 { field1 : i32, @@ -73,4 +73,4 @@ impl From< &Options2 > for String // -include!( "./only_test/components_components_assign.rs" ); +include!( "./only_test/components_assign.rs" ); diff --git a/module/core/former/tests/inc/components_tests/components_assign_manual.rs b/module/core/former/tests/inc/components_tests/components_assign_manual.rs index 182ad0dacf..bc88f29e14 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 @@ -1,7 +1,7 @@ #[ allow( unused_imports ) ] use super::*; #[ allow( unused_imports ) ] -use former::{ ComponentAssign, AssignWithType }; +use former::{ Assign, AssignWithType }; /// /// Options1 @@ -42,7 +42,7 @@ impl From< &Options1 > for f32 } } -impl< IntoT > former::ComponentAssign< i32, IntoT > for Options1 +impl< IntoT > former::Assign< i32, IntoT > for Options1 where IntoT : Into< i32 >, { @@ -53,7 +53,7 @@ where } } -impl< IntoT > former::ComponentAssign< String, IntoT > for Options1 +impl< IntoT > former::Assign< String, IntoT > for Options1 where IntoT : Into< String >, { @@ -64,7 +64,7 @@ where } } -impl< IntoT > former::ComponentAssign< f32, IntoT > for Options1 +impl< IntoT > former::Assign< f32, IntoT > for Options1 where IntoT : Into< f32 >, { @@ -93,9 +93,9 @@ where // #[ allow( dead_code ) ] impl< T, IntoT > Options1ComponentsAssign< IntoT > for T where - T : former::ComponentAssign< i32, IntoT >, - T : former::ComponentAssign< String, IntoT >, - T : former::ComponentAssign< f32, IntoT >, + T : former::Assign< i32, IntoT >, + T : former::Assign< String, IntoT >, + T : former::Assign< f32, IntoT >, IntoT : Into< i32 >, IntoT : Into< String >, IntoT : Into< f32 >, @@ -104,9 +104,9 @@ where #[ inline( always ) ] fn options_1_assign( &mut self, component : IntoT ) { - former::ComponentAssign::< i32, _ >::assign( self, component.clone() ); - former::ComponentAssign::< String, _ >::assign( self, component.clone() ); - former::ComponentAssign::< f32, _ >::assign( self, component.clone() ); + former::Assign::< i32, _ >::assign( self, component.clone() ); + former::Assign::< String, _ >::assign( self, component.clone() ); + former::Assign::< f32, _ >::assign( self, component.clone() ); } } @@ -139,7 +139,7 @@ impl From< &Options2 > for String } } -impl< IntoT > former::ComponentAssign< i32, IntoT > for Options2 +impl< IntoT > former::Assign< i32, IntoT > for Options2 where IntoT : Into< i32 >, { @@ -150,7 +150,7 @@ where } } -impl< IntoT > former::ComponentAssign< String, IntoT > for Options2 +impl< IntoT > former::Assign< String, IntoT > for Options2 where IntoT : Into< String >, { @@ -176,8 +176,8 @@ where impl< T, IntoT > Options2ComponentsAssign< IntoT > for T where - T : former::ComponentAssign< i32, IntoT >, - T : former::ComponentAssign< String, IntoT >, + T : former::Assign< i32, IntoT >, + T : former::Assign< String, IntoT >, IntoT : Into< i32 >, IntoT : Into< String >, IntoT : Clone, @@ -185,11 +185,11 @@ where #[ inline( always ) ] fn options_2_assign( &mut self, component : IntoT ) { - former::ComponentAssign::< i32, _ >::assign( self, component.clone() ); - former::ComponentAssign::< String, _ >::assign( self, component.clone() ); + former::Assign::< i32, _ >::assign( self, component.clone() ); + former::Assign::< String, _ >::assign( self, component.clone() ); } } // -include!( "./only_test/components_components_assign.rs" ); +include!( "./only_test/components_assign.rs" ); diff --git a/module/core/former/tests/inc/components_tests/composite.rs b/module/core/former/tests/inc/components_tests/composite.rs index 7105ba0b0e..091fcc268b 100644 --- a/module/core/former/tests/inc/components_tests/composite.rs +++ b/module/core/former/tests/inc/components_tests/composite.rs @@ -1,7 +1,7 @@ #[ allow( unused_imports ) ] use super::*; #[ allow( unused_imports ) ] -use former::{ ComponentAssign, AssignWithType }; +use former::{ Assign, AssignWithType }; /// /// Options1 @@ -14,7 +14,7 @@ use former::{ ComponentAssign, AssignWithType }; Default, PartialEq, the_module::ComponentFrom, - the_module::ComponentAssign, + the_module::Assign, the_module::ComponentsAssign, the_module::FromComponents, ) @@ -38,7 +38,7 @@ pub struct Options1 Default, PartialEq, the_module::ComponentFrom, - the_module::ComponentAssign, + the_module::Assign, the_module::ComponentsAssign, the_module::FromComponents, ) @@ -72,4 +72,4 @@ pub struct Options2 // -include!( "./only_test/components_composite.rs" ); +include!( "./only_test/composite.rs" ); diff --git a/module/core/former/tests/inc/components_tests/composite_manual.rs b/module/core/former/tests/inc/components_tests/composite_manual.rs index 34e77f09af..276def66ae 100644 --- a/module/core/former/tests/inc/components_tests/composite_manual.rs +++ b/module/core/former/tests/inc/components_tests/composite_manual.rs @@ -1,7 +1,7 @@ #[ allow( unused_imports ) ] use super::*; #[ allow( unused_imports ) ] -use former::{ ComponentAssign, AssignWithType }; +use former::{ Assign, AssignWithType }; /// /// Options1 @@ -42,7 +42,7 @@ impl From< &Options1 > for f32 } } -impl< IntoT > former::ComponentAssign< i32, IntoT > for Options1 +impl< IntoT > former::Assign< i32, IntoT > for Options1 where IntoT : Into< i32 >, { @@ -53,7 +53,7 @@ where } } -impl< IntoT > former::ComponentAssign< String, IntoT > for Options1 +impl< IntoT > former::Assign< String, IntoT > for Options1 where IntoT : Into< String >, { @@ -64,7 +64,7 @@ where } } -impl< IntoT > former::ComponentAssign< f32, IntoT > for Options1 +impl< IntoT > former::Assign< f32, IntoT > for Options1 where IntoT : Into< f32 >, { @@ -91,9 +91,9 @@ where impl< T, IntoT > Options1ComponentsAssign< IntoT > for T where - T : former::ComponentAssign< i32, IntoT >, - T : former::ComponentAssign< String, IntoT >, - T : former::ComponentAssign< f32, IntoT >, + T : former::Assign< i32, IntoT >, + T : former::Assign< String, IntoT >, + T : former::Assign< f32, IntoT >, IntoT : Into< i32 >, IntoT : Into< String >, IntoT : Into< f32 >, @@ -102,9 +102,9 @@ where #[ inline( always ) ] fn options_1_assign( &mut self, component : IntoT ) { - former::ComponentAssign::< i32, _ >::assign( self, component.clone() ); - former::ComponentAssign::< String, _ >::assign( self, component.clone() ); - former::ComponentAssign::< f32, _ >::assign( self, component.clone() ); + former::Assign::< i32, _ >::assign( self, component.clone() ); + former::Assign::< String, _ >::assign( self, component.clone() ); + former::Assign::< f32, _ >::assign( self, component.clone() ); } } @@ -137,7 +137,7 @@ impl From< &Options2 > for String } } -impl< IntoT > former::ComponentAssign< i32, IntoT > for Options2 +impl< IntoT > former::Assign< i32, IntoT > for Options2 where IntoT : Into< i32 >, { @@ -148,7 +148,7 @@ where } } -impl< IntoT > former::ComponentAssign< String, IntoT > for Options2 +impl< IntoT > former::Assign< String, IntoT > for Options2 where IntoT : Into< String >, { @@ -174,8 +174,8 @@ where impl< T, IntoT > Options2ComponentsAssign< IntoT > for T where - T : former::ComponentAssign< i32, IntoT >, - T : former::ComponentAssign< String, IntoT >, + T : former::Assign< i32, IntoT >, + T : former::Assign< String, IntoT >, IntoT : Into< i32 >, IntoT : Into< String >, IntoT : Clone, @@ -183,8 +183,8 @@ where #[ inline( always ) ] fn options_2_assign( &mut self, component : IntoT ) { - former::ComponentAssign::< i32, _ >::assign( self, component.clone() ); - former::ComponentAssign::< String, _ >::assign( self, component.clone() ); + former::Assign::< i32, _ >::assign( self, component.clone() ); + former::Assign::< String, _ >::assign( self, component.clone() ); } } @@ -209,4 +209,4 @@ where // -include!( "./only_test/components_composite.rs" ); +include!( "./only_test/composite.rs" ); diff --git a/module/core/former/tests/inc/components_tests/from_components.rs b/module/core/former/tests/inc/components_tests/from_components.rs index 9f69aab624..2105667d9f 100644 --- a/module/core/former/tests/inc/components_tests/from_components.rs +++ b/module/core/former/tests/inc/components_tests/from_components.rs @@ -72,4 +72,4 @@ pub struct Options2 // -include!( "./only_test/components_from_components.rs" ); +include!( "./only_test/from_components.rs" ); diff --git a/module/core/former/tests/inc/components_tests/from_components_manual.rs b/module/core/former/tests/inc/components_tests/from_components_manual.rs index 6a6c29e323..edd26c9c80 100644 --- a/module/core/former/tests/inc/components_tests/from_components_manual.rs +++ b/module/core/former/tests/inc/components_tests/from_components_manual.rs @@ -72,4 +72,4 @@ where // -include!( "./only_test/components_from_components.rs" ); +include!( "./only_test/from_components.rs" ); diff --git a/module/core/former/tests/inc/components_tests/only_test/components_component_assign.rs b/module/core/former/tests/inc/components_tests/only_test/component_assign.rs similarity index 100% rename from module/core/former/tests/inc/components_tests/only_test/components_component_assign.rs rename to module/core/former/tests/inc/components_tests/only_test/component_assign.rs diff --git a/module/core/former/tests/inc/components_tests/only_test/components_component_from.rs b/module/core/former/tests/inc/components_tests/only_test/component_from.rs similarity index 100% rename from module/core/former/tests/inc/components_tests/only_test/components_component_from.rs rename to module/core/former/tests/inc/components_tests/only_test/component_from.rs diff --git a/module/core/former/tests/inc/components_tests/only_test/components_components_assign.rs b/module/core/former/tests/inc/components_tests/only_test/components_assign.rs similarity index 96% rename from module/core/former/tests/inc/components_tests/only_test/components_components_assign.rs rename to module/core/former/tests/inc/components_tests/only_test/components_assign.rs index ecba41850c..37d11147aa 100644 --- a/module/core/former/tests/inc/components_tests/only_test/components_components_assign.rs +++ b/module/core/former/tests/inc/components_tests/only_test/components_assign.rs @@ -7,7 +7,7 @@ fn component_assign() let mut o2 = Options2::default(); o2.assign( 42 ); o2.assign( "Hello, world!" ); - println!( "field1: {}, field2: {}", o2.field1, o2.field2 ); + println!( "field1 : {}, field2 : {}", o2.field1, o2.field2 ); let exp = Options2 { field1 : 42, field2 : "Hello, world!".to_string() }; assert_eq!( o2, exp ); diff --git a/module/core/former/tests/inc/components_tests/only_test/components_composite.rs b/module/core/former/tests/inc/components_tests/only_test/composite.rs similarity index 100% rename from module/core/former/tests/inc/components_tests/only_test/components_composite.rs rename to module/core/former/tests/inc/components_tests/only_test/composite.rs diff --git a/module/core/former/tests/inc/components_tests/only_test/components_from_components.rs b/module/core/former/tests/inc/components_tests/only_test/from_components.rs similarity index 100% rename from module/core/former/tests/inc/components_tests/only_test/components_from_components.rs rename to module/core/former/tests/inc/components_tests/only_test/from_components.rs diff --git a/module/core/former/tests/inc/former_tests/a_primitives_manual.rs b/module/core/former/tests/inc/former_tests/a_primitives_manual.rs index 90e1290d6b..baafc6e1ae 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 @@ -203,7 +203,6 @@ where return result; } - // xxx : update description #[ inline( always ) ] pub fn new( on_end : Definition::End ) -> Self { diff --git a/module/core/former/tests/inc/former_tests/attribute_default_collection.rs b/module/core/former/tests/inc/former_tests/attribute_default_collection.rs new file mode 100644 index 0000000000..3129fe839d --- /dev/null +++ b/module/core/former/tests/inc/former_tests/attribute_default_collection.rs @@ -0,0 +1,52 @@ +#[ allow( unused_imports ) ] +use super::*; + +use collection_tools::HashMap; +use collection_tools::HashSet; + +#[ derive( Debug, PartialEq, the_module::Former ) ] +pub struct Struct1 +{ + + #[ former( default = collection_tools::vec![ 1, 2, 3 ] ) ] + vec_ints : Vec< i32 >, + #[ former( default = collection_tools::hmap!{ 1 => 11 } ) ] + hashmap_ints : HashMap< i32, i32 >, + #[ former( default = collection_tools::hset!{ 11 } ) ] + hashset_ints : HashSet< i32 >, + + #[ former( default = collection_tools::vec![ "abc".to_string(), "def".to_string() ] ) ] + vec_strings : Vec< String >, + #[ former( default = collection_tools::hmap!{ "k1".to_string() => "v1".to_string() } ) ] + hashmap_strings : HashMap< String, String >, + #[ former( default = collection_tools::hset!{ "k1".to_string() } ) ] + hashset_strings : HashSet< String >, + +} + +// + +tests_impls! +{ + fn test_complex() + { + let command = Struct1::former().form(); + let expected = Struct1 + { + vec_ints : collection_tools::vec![ 1, 2, 3 ], + hashmap_ints : collection_tools::hmap!{ 1 => 11 }, + hashset_ints : collection_tools::hset!{ 11 }, + vec_strings : collection_tools::vec![ "abc".to_string(), "def".to_string() ], + hashmap_strings : collection_tools::hmap!{ "k1".to_string() => "v1".to_string() }, + hashset_strings : collection_tools::hset!{ "k1".to_string() }, + }; + 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 deleted file mode 100644 index a1e1e07132..0000000000 --- a/module/core/former/tests/inc/former_tests/attribute_default_container.rs +++ /dev/null @@ -1,52 +0,0 @@ -#[ allow( unused_imports ) ] -use super::*; - -use std::collections::HashMap; -use std::collections::HashSet; - -#[ derive( Debug, PartialEq, the_module::Former ) ] -pub struct Struct1 -{ - - #[ former( default = vec![ 1, 2, 3 ] ) ] - vec_ints : Vec< i32 >, - #[ former( default = hmap!{ 1 => 11 } ) ] - hashmap_ints : HashMap< i32, i32 >, - #[ former( default = hset!{ 11 } ) ] - hashset_ints : HashSet< i32 >, - - #[ former( default = vec![ "abc".to_string(), "def".to_string() ] ) ] - vec_strings : Vec< String >, - #[ former( default = hmap!{ "k1".to_string() => "v1".to_string() } ) ] - hashmap_strings : HashMap< String, String >, - #[ former( default = hset!{ "k1".to_string() } ) ] - hashset_strings : HashSet< String >, - -} - -// - -tests_impls! -{ - fn test_complex() - { - let command = Struct1::former().form(); - let expected = Struct1 - { - vec_ints : vec![ 1, 2, 3 ], - hashmap_ints : hmap!{ 1 => 11 }, - hashset_ints : hset!{ 11 }, - vec_strings : vec![ "abc".to_string(), "def".to_string() ], - hashmap_strings : hmap!{ "k1".to_string() => "v1".to_string() }, - hashset_strings : hset!{ "k1".to_string() }, - }; - a_id!( command, expected ); - } -} - -// - -tests_index! -{ - test_complex, -} 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 609915ad5a..6636695537 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,8 +1,8 @@ #[ allow( unused_imports ) ] use super::*; -use std::collections::HashMap; -use std::collections::HashSet; +use collection_tools::HashMap; +use collection_tools::HashSet; #[ derive( Debug, PartialEq, the_module::Former ) ] pub struct Struct1 @@ -35,9 +35,9 @@ tests_impls! string_1 : "abc".to_string(), int_optional_1 : Some( 31 ), string_optional_1 : Some( "abc".to_string() ), - vec_1 : vec![], - hashmap_1 : hmap!{}, - hashset_1 : hset!{}, + vec_1 : collection_tools::vec![], + hashmap_1 : collection_tools::hmap!{}, + hashset_1 : collection_tools::hset!{}, }; a_id!( command, expected ); } diff --git a/module/core/former/tests/inc/former_tests/attribute_multiple.rs b/module/core/former/tests/inc/former_tests/attribute_multiple.rs new file mode 100644 index 0000000000..55c3745e8d --- /dev/null +++ b/module/core/former/tests/inc/former_tests/attribute_multiple.rs @@ -0,0 +1,34 @@ +#[ allow( unused_imports ) ] +use super::*; + +#[ derive( Debug, PartialEq, the_module::Former ) ] +pub struct Struct1 +{ + + #[ former( default = collection_tools::vec![ 1, 2, 3 ] ) ] + #[ former( default = collection_tools::vec![ 2, 3, 4 ] ) ] + vec_ints : Vec< i32 >, + +} + +// + +tests_impls! +{ + fn test_complex() + { + let command = Struct1::former().form(); + let expected = Struct1 + { + vec_ints : collection_tools::vec![ 2, 3, 4 ], + }; + a_id!( command, expected ); + } +} + +// + +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 index 57936294d3..983fbc655e 100644 --- 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 @@ -3,7 +3,7 @@ use super::*; #[ derive( Debug, PartialEq, the_module::Former ) ] #[ storage_fields( a : i32, b : Option< String > ) ] -#[ mutator( custom = true ) ] +#[ mutator( custom ) ] // #[ debug ] // #[ derive( Debug, PartialEq ) ] pub struct Struct1 diff --git a/module/core/former/tests/inc/former_tests/collection_former_binary_heap.rs b/module/core/former/tests/inc/former_tests/collection_former_binary_heap.rs new file mode 100644 index 0000000000..354585ec10 --- /dev/null +++ b/module/core/former/tests/inc/former_tests/collection_former_binary_heap.rs @@ -0,0 +1,207 @@ +#![ allow( dead_code ) ] + +#[ allow( unused_imports ) ] +use super::*; +#[ allow( unused_imports ) ] +use collection_tools::BinaryHeap; + +#[ test ] +fn add() +{ + + // explicit with CollectionFormer + + let got : BinaryHeap< String > = the_module + ::CollectionFormer + ::< String, former::BinaryHeapDefinition< String, (), BinaryHeap< String >, the_module::ReturnStorage > > + ::new( former::ReturnStorage ) + .add( "a" ) + .add( "b" ) + .form(); + let exp = collection_tools::heap! + [ + "a".to_string(), + "b".to_string(), + ]; + a_id!( got.into_sorted_vec(), exp.into_sorted_vec() ); + + // explicit with BinaryHeapFormer + + let got : BinaryHeap< String > = the_module::BinaryHeapFormer::< String, (), BinaryHeap< String >, the_module::ReturnStorage > + ::new( former::ReturnStorage ) + .add( "a" ) + .add( "b" ) + .form(); + let exp = collection_tools::heap! + [ + "a".to_string(), + "b".to_string(), + ]; + a_id!( got.into_sorted_vec(), exp.into_sorted_vec() ); + + // compact with BinaryHeapFormer + + let got : BinaryHeap< String > = the_module::BinaryHeapFormer::new( former::ReturnStorage ) + .add( "a" ) + .add( "b" ) + .form(); + let exp = collection_tools::heap! + [ + "a".to_string(), + "b".to_string(), + ]; + a_id!( got.into_sorted_vec(), exp.into_sorted_vec() ); + + // with begin_coercing + + let got : BinaryHeap< String > = the_module::BinaryHeapFormer + ::begin( Some( collection_tools::heap![ "a".to_string() ] ), Some( () ), former::ReturnStorage ) + .add( "b" ) + .form(); + let exp = collection_tools::heap! + [ + "a".to_string(), + "b".to_string(), + ]; + a_id!( got.into_sorted_vec(), exp.into_sorted_vec() ); + + // with help of ext + + use the_module::BinaryHeapExt; + let got : BinaryHeap< String > = BinaryHeap::former() + .add( "a" ) + .add( "b" ) + .form(); + let exp = collection_tools::heap! + [ + "a".to_string(), + "b".to_string(), + ]; + a_id!( got.into_sorted_vec(), exp.into_sorted_vec() ); + + // + +} + +// qqq : zzz : remove #[ cfg( not( feature = "use_alloc" ) ) ] -- done +// #[ cfg( not( feature = "use_alloc" ) ) ] +#[ test ] +fn replace() +{ + + let got : BinaryHeap< String > = the_module::BinaryHeapFormer::new( former::ReturnStorage ) + .add( "x" ) + .replace( collection_tools::heap![ "a".to_string(), "b".to_string() ] ) + .form(); + let exp = collection_tools::heap! + [ + "a".to_string(), + "b".to_string(), + ]; + a_id!( got.into_sorted_vec(), exp.into_sorted_vec() ); + +} + +#[ test ] +fn entity_to() +{ + + let got = < BinaryHeap< i32 > as former::EntityToFormer< former::BinaryHeapDefinition< i32, (), BinaryHeap< i32 >, former::ReturnStorage > > > + ::Former::new( former::ReturnStorage ) + .add( 13 ) + .form(); + let exp = collection_tools::heap![ 13 ]; + a_id!( got.into_sorted_vec(), exp.into_sorted_vec() ); + + let got = < BinaryHeap< i32 > as former::EntityToStorage >::Storage::default(); + let exp = + < + BinaryHeap< i32 > as former::EntityToFormer + < + former::BinaryHeapDefinition + < + i32, + (), + BinaryHeap< i32 >, + former::ReturnStorage, + > + > + >::Former::new( former::ReturnStorage ) + .form(); + a_id!( got.into_sorted_vec(), exp.into_sorted_vec() ); + + let got = < BinaryHeap< i32 > as former::EntityToStorage >::Storage::default(); + let exp = + < + BinaryHeap< i32 > as former::EntityToFormer + < + < BinaryHeap< i32 > as former::EntityToDefinition< (), BinaryHeap< i32 >, former::ReturnPreformed > >::Definition + > + >::Former::new( former::ReturnPreformed ) + .form(); + a_id!( got.into_sorted_vec(), exp.into_sorted_vec() ); + +} + +#[ test ] +fn entry_to_val() +{ + let got = former::EntryToVal::< BinaryHeap< i32 > >::entry_to_val( 13i32 ); + let exp = 13i32; + a_id!( got, exp ) +} + +#[ test ] +fn val_to_entry() +{ + let got = former::ValToEntry::< BinaryHeap< i32 > >::val_to_entry( 13i32 ); + let exp = 13i32; + a_id!( got, exp ) +} + +#[ test ] +fn subformer() +{ + + /// Parameter description. + #[ derive( Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, the_module::Former ) ] + pub struct Child + { + name : String, + data : bool, + } + + /// Parent required for the template. + #[ derive( Debug, Default, the_module::Former ) ] + // #[ derive( Debug, Default, PartialEq, the_module::Former ) ] #[ debug ] + // #[ derive( Debug, Default, PartialEq ) ] + pub struct Parent + { + #[ subform_collection( definition = former::BinaryHeapDefinition ) ] + children : BinaryHeap< Child >, + } + + impl PartialEq< Parent > for Parent + { + fn eq( &self, other : &Parent ) -> bool + { + self.children.clone().into_sorted_vec() == other.children.clone().into_sorted_vec() + } + } + + let got = Parent::former() + .children() + .add( Child::former().name( "a" ).form() ) + .add( Child::former().name( "b" ).form() ) + .end() + .form(); + + let children = collection_tools::heap! + [ + 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/collection_former_btree_map.rs b/module/core/former/tests/inc/former_tests/collection_former_btree_map.rs new file mode 100644 index 0000000000..3bce14765f --- /dev/null +++ b/module/core/former/tests/inc/former_tests/collection_former_btree_map.rs @@ -0,0 +1,221 @@ +#![ allow( dead_code ) ] + +#[ allow( unused_imports ) ] +use super::*; +#[ allow( unused_imports ) ] +use collection_tools::BTreeMap; + +// qqq : zzz : remove #[ cfg( not( feature = "use_alloc" ) ) ] -- done +// #[ cfg( not( feature = "use_alloc" ) ) ] +#[ test ] +fn add() +{ + + // expliccit with CollectionFormer + + let got : BTreeMap< String, String > = the_module + ::CollectionFormer + ::< ( String, String ), former::BTreeMapDefinition< String, String, (), BTreeMap< String, String >, the_module::ReturnStorage > > + ::new( former::ReturnStorage ) + .add( ( "a".into(), "x".into() ) ) + .add( ( "b".into(), "y".into() ) ) + .form(); + let exp = collection_tools::bmap! + [ + "a".to_string() => "x".to_string(), + "b".to_string() => "y".to_string(), + ]; + a_id!( got, exp ); + + // expliccit with BTreeMapFormer + + let got : BTreeMap< String, String > = the_module::BTreeMapFormer::< String, String, (), BTreeMap< String, String >, the_module::ReturnStorage > + ::new( former::ReturnStorage ) + .add( ( "a".into(), "x".into() ) ) + .add( ( "b".into(), "y".into() ) ) + .form(); + let exp = collection_tools::bmap! + [ + "a".to_string() => "x".to_string(), + "b".to_string() => "y".to_string(), + ]; + a_id!( got, exp ); + + // compact with BTreeMapFormer + + let got : BTreeMap< String, String > = the_module::BTreeMapFormer::new( former::ReturnStorage ) + .add( ( "a".into(), "x".into() ) ) + .add( ( "b".into(), "y".into() ) ) + .form(); + let exp = collection_tools::bmap! + [ + "a".to_string() => "x".to_string(), + "b".to_string() => "y".to_string(), + ]; + a_id!( got, exp ); + + // with begin + + let got : BTreeMap< String, String > = the_module::BTreeMapFormer + ::begin( Some( collection_tools::bmap![ "a".to_string() => "x".to_string() ] ), Some( () ), former::ReturnStorage ) + .add( ( "b".into(), "y".into() ) ) + .form(); + let exp = collection_tools::bmap! + [ + "a".to_string() => "x".to_string(), + "b".to_string() => "y".to_string(), + ]; + a_id!( got, exp ); + + // with help of ext + + use the_module::BTreeMapExt; + let got : BTreeMap< String, String > = BTreeMap::former() + .add( ( "a".into(), "x".into() ) ) + .add( ( "b".into(), "y".into() ) ) + .form(); + let exp = collection_tools::bmap! + [ + "a".to_string() => "x".to_string(), + "b".to_string() => "y".to_string(), + ]; + a_id!( got, exp ); + + // + +} + +// qqq : zzz : remove #[ cfg( not( feature = "use_alloc" ) ) ] -- done +// #[ cfg( not( feature = "use_alloc" ) ) ] +#[ test ] +fn replace() +{ + + let got : BTreeMap< String, String > = the_module::BTreeMapFormer::new( former::ReturnStorage ) + .add( ( "x".to_string(), "y".to_string() ) ) + .replace( collection_tools::bmap![ "a".to_string() => "x".to_string(), "b".to_string() => "y".to_string(), ] ) + .form(); + let exp = collection_tools::bmap! + [ + "a".to_string() => "x".to_string(), + "b".to_string() => "y".to_string(), + ]; + a_id!( got, exp ); + +} + +#[ test ] +fn entity_to() +{ + + let got = < BTreeMap< i32, i32 > as former::EntityToFormer< former::BTreeMapDefinition< i32, i32, (), BTreeMap< i32, i32 >, former::ReturnStorage > > > + ::Former::new( former::ReturnStorage ) + .add( ( 13, 14 ) ) + .form(); + let exp = collection_tools::bmap![ 13 => 14 ]; + a_id!( got, exp ); + + let got = < BTreeMap< i32, i32 > as former::EntityToStorage >::Storage::default(); + let exp = + < + BTreeMap< i32, i32 > as former::EntityToFormer + < + former::BTreeMapDefinition + < + i32, + i32, + (), + BTreeMap< i32, i32 >, + former::ReturnStorage, + > + > + >::Former::new( former::ReturnStorage ) + .form(); + a_id!( got, exp ); + + let got = < BTreeMap< i32, i32 > as former::EntityToStorage >::Storage::default(); + let exp = + < + BTreeMap< i32, i32 > as former::EntityToFormer + < + < BTreeMap< i32, i32 > as former::EntityToDefinition< (), BTreeMap< i32, i32 >, former::ReturnPreformed > >::Definition + > + >::Former::new( former::ReturnPreformed ) + .form(); + a_id!( got, exp ); + +} + +#[ test ] +fn entry_to_val() +{ + let got = former::EntryToVal::< BTreeMap< 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< BTreeMap< u32, Val > > for Val + { + type Entry = ( u32, Val ); + #[ inline( always ) ] + fn val_to_entry( self ) -> Self::Entry + { + ( self.key, self ) + } + } + + let got = former::ValToEntry::< BTreeMap< u32, Val > >::val_to_entry( Val { key : 1u32, data : 13i32 } ); + let exp = ( 1u32, Val { key : 1u32, data : 13i32 } ); + a_id!( got, exp ) + +} + +#[ test ] +fn subformer() +{ + + /// 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_collection( definition = former::BTreeMapDefinition ) ] + children : BTreeMap< u32, Child >, + } + + let got = Parent::former() + .children() + .add( ( 0, Child::former().name( "a" ).form() ) ) + .add( ( 1, Child::former().name( "b" ).form() ) ) + .end() + .form(); + + let children = collection_tools::bmap! + [ + 0 => Child { name : "a".to_string(), data : false }, + 1 => 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/collection_former_btree_set.rs b/module/core/former/tests/inc/former_tests/collection_former_btree_set.rs new file mode 100644 index 0000000000..310b12b710 --- /dev/null +++ b/module/core/former/tests/inc/former_tests/collection_former_btree_set.rs @@ -0,0 +1,199 @@ +#![ allow( dead_code ) ] + +#[ allow( unused_imports ) ] +use super::*; +#[ allow( unused_imports ) ] +use collection_tools::BTreeSet; + +#[ test ] +fn add() +{ + + // explicit with CollectionFormer + + let got : BTreeSet< String > = the_module + ::CollectionFormer + ::< String, former::BTreeSetDefinition< String, (), BTreeSet< String >, the_module::ReturnStorage > > + ::new( former::ReturnStorage ) + .add( "a" ) + .add( "b" ) + .form(); + let exp = collection_tools::bset! + [ + "a".to_string(), + "b".to_string(), + ]; + a_id!( got, exp ); + + // explicit with BTreeSetFormer + + let got : BTreeSet< String > = the_module::BTreeSetFormer::< String, (), BTreeSet< String >, the_module::ReturnStorage > + ::new( former::ReturnStorage ) + .add( "a" ) + .add( "b" ) + .form(); + let exp = collection_tools::bset! + [ + "a".to_string(), + "b".to_string(), + ]; + a_id!( got, exp ); + + // compact with BTreeSetFormer + + let got : BTreeSet< String > = the_module::BTreeSetFormer::new( former::ReturnStorage ) + .add( "a" ) + .add( "b" ) + .form(); + let exp = collection_tools::bset! + [ + "a".to_string(), + "b".to_string(), + ]; + a_id!( got, exp ); + + // with begin_coercing + + let got : BTreeSet< String > = the_module::BTreeSetFormer + ::begin( Some( collection_tools::bset![ "a".to_string() ] ), Some( () ), former::ReturnStorage ) + .add( "b" ) + .form(); + let exp = collection_tools::bset! + [ + "a".to_string(), + "b".to_string(), + ]; + a_id!( got, exp ); + + // with help of ext + + use the_module::BTreeSetExt; + let got : BTreeSet< String > = BTreeSet::former() + .add( "a" ) + .add( "b" ) + .form(); + let exp = collection_tools::bset! + [ + "a".to_string(), + "b".to_string(), + ]; + a_id!( got, exp ); + + // + +} + +// qqq : zzz : remove #[ cfg( not( feature = "use_alloc" ) ) ] -- done +// #[ cfg( not( feature = "use_alloc" ) ) ] +#[ test ] +fn replace() +{ + + let got : BTreeSet< String > = the_module::BTreeSetFormer::new( former::ReturnStorage ) + .add( "x" ) + .replace( collection_tools::bset![ "a".to_string(), "b".to_string() ] ) + .form(); + let exp = collection_tools::bset! + [ + "a".to_string(), + "b".to_string(), + ]; + a_id!( got, exp ); + +} + +#[ test ] +fn entity_to() +{ + + let got = < BTreeSet< i32 > as former::EntityToFormer< former::BTreeSetDefinition< i32, (), BTreeSet< i32 >, former::ReturnStorage > > > + ::Former::new( former::ReturnStorage ) + .add( 13 ) + .form(); + let exp = collection_tools::bset![ 13 ]; + a_id!( got, exp ); + + let got = < BTreeSet< i32 > as former::EntityToStorage >::Storage::default(); + let exp = + < + BTreeSet< i32 > as former::EntityToFormer + < + former::BTreeSetDefinition + < + i32, + (), + BTreeSet< i32 >, + former::ReturnStorage, + > + > + >::Former::new( former::ReturnStorage ) + .form(); + a_id!( got, exp ); + + let got = < BTreeSet< i32 > as former::EntityToStorage >::Storage::default(); + let exp = + < + BTreeSet< i32 > as former::EntityToFormer + < + < BTreeSet< i32 > as former::EntityToDefinition< (), BTreeSet< i32 >, former::ReturnPreformed > >::Definition + > + >::Former::new( former::ReturnPreformed ) + .form(); + a_id!( got, exp ); + +} + +#[ test ] +fn entry_to_val() +{ + let got = former::EntryToVal::< BTreeSet< i32 > >::entry_to_val( 13i32 ); + let exp = 13i32; + a_id!( got, exp ) +} + +#[ test ] +fn val_to_entry() +{ + let got = former::ValToEntry::< BTreeSet< i32 > >::val_to_entry( 13i32 ); + let exp = 13i32; + a_id!( got, exp ) +} + +#[ test ] +fn subformer() +{ + + /// Parameter description. + #[ derive( Debug, Default, PartialEq, Eq, PartialOrd, Ord, 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_collection( definition = former::BTreeSetDefinition ) ] + children : BTreeSet< Child >, + } + + let got = Parent::former() + .children() + .add( Child::former().name( "a" ).form() ) + .add( Child::former().name( "b" ).form() ) + .end() + .form(); + + let children = collection_tools::bset! + [ + 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/container_former_common.rs b/module/core/former/tests/inc/former_tests/collection_former_common.rs similarity index 85% rename from module/core/former/tests/inc/former_tests/container_former_common.rs rename to module/core/former/tests/inc/former_tests/collection_former_common.rs index 3bc9c84dfc..80ed29689e 100644 --- a/module/core/former/tests/inc/former_tests/container_former_common.rs +++ b/module/core/former/tests/inc/former_tests/collection_former_common.rs @@ -132,14 +132,14 @@ fn custom_definition() // - let got = former::ContainerFormer::< String, Return13 >::begin( None, None, Return13 ) + let got = former::CollectionFormer::< 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 ) + let got = former::CollectionFormer::< String, Return13 >::new( Return13 ) .add( "a" ) .add( "b" ) .form(); @@ -205,14 +205,14 @@ fn custom_definition_parametrized() // - let got = the_module::ContainerFormer::< String, Return13< String > >::begin_coercing( None, None, Return13::new() ) + let got = the_module::CollectionFormer::< 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() ) + let got = the_module::CollectionFormer::< String, Return13< String > >::new_coercing( Return13::new() ) .add( "a" ) .add( "b" ) .form(); @@ -221,16 +221,16 @@ fn custom_definition_parametrized() // - type MyContainer< E > = the_module::ContainerFormer::< E, Return13< E > >; + type MyCollection< E > = the_module::CollectionFormer::< E, Return13< E > >; - let got = MyContainer::< String >::begin_coercing( None, None, Return13::new() ) + let got = MyCollection::< 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() ) + let got = MyCollection::< String >::new_coercing( Return13::new() ) .add( "a" ) .add( "b" ) .form(); @@ -273,21 +273,21 @@ fn custom_definition_custom_end() } let end_wrapper : the_module::FormingEndClosure< Return13 > = the_module::FormingEndClosure::new( return_13 ); - let got = the_module::ContainerFormer::< String, Return13 >::new( end_wrapper ) + let got = the_module::CollectionFormer::< 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() ) + let got = the_module::CollectionFormer::< 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 ) + let got = the_module::CollectionFormer::< String, Return13 >::new_coercing( return_13 ) .add( "a" ) .add( "b" ) .form(); diff --git a/module/core/former/tests/inc/former_tests/container_former_hashmap.rs b/module/core/former/tests/inc/former_tests/collection_former_hashmap.rs similarity index 50% rename from module/core/former/tests/inc/former_tests/container_former_hashmap.rs rename to module/core/former/tests/inc/former_tests/collection_former_hashmap.rs index 1b34ebbb3b..365a26b23e 100644 --- a/module/core/former/tests/inc/former_tests/container_former_hashmap.rs +++ b/module/core/former/tests/inc/former_tests/collection_former_hashmap.rs @@ -5,22 +5,22 @@ use super::*; #[ allow( unused_imports ) ] use collection_tools::HashMap; -// qqq : zzz : remove #[ cfg( not( feature = "use_alloc" ) ) ] -#[ cfg( not( feature = "use_alloc" ) ) ] +// qqq : zzz : remove #[ cfg( not( feature = "use_alloc" ) ) ] -- done +// #[ cfg( not( feature = "use_alloc" ) ) ] #[ test ] fn add() { - // expliccit with ContainerFormer + // expliccit with CollectionFormer let got : HashMap< String, String > = the_module - ::ContainerFormer + ::CollectionFormer ::< ( 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! + let exp = collection_tools::hmap! [ "a".to_string() => "x".to_string(), "b".to_string() => "y".to_string(), @@ -34,7 +34,7 @@ fn add() .add( ( "a".into(), "x".into() ) ) .add( ( "b".into(), "y".into() ) ) .form(); - let exp = hmap! + let exp = collection_tools::hmap! [ "a".to_string() => "x".to_string(), "b".to_string() => "y".to_string(), @@ -47,7 +47,7 @@ fn add() .add( ( "a".into(), "x".into() ) ) .add( ( "b".into(), "y".into() ) ) .form(); - let exp = hmap! + let exp = collection_tools::hmap! [ "a".to_string() => "x".to_string(), "b".to_string() => "y".to_string(), @@ -57,10 +57,10 @@ fn add() // with begin let got : HashMap< String, String > = the_module::HashMapFormer - ::begin( Some( hmap![ "a".to_string() => "x".to_string() ] ), Some( () ), former::ReturnStorage ) + ::begin( Some( collection_tools::hmap![ "a".to_string() => "x".to_string() ] ), Some( () ), former::ReturnStorage ) .add( ( "b".into(), "y".into() ) ) .form(); - let exp = hmap! + let exp = collection_tools::hmap! [ "a".to_string() => "x".to_string(), "b".to_string() => "y".to_string(), @@ -74,7 +74,7 @@ fn add() .add( ( "a".into(), "x".into() ) ) .add( ( "b".into(), "y".into() ) ) .form(); - let exp = hmap! + let exp = collection_tools::hmap! [ "a".to_string() => "x".to_string(), "b".to_string() => "y".to_string(), @@ -85,17 +85,17 @@ fn add() } -// qqq : zzz : remove #[ cfg( not( feature = "use_alloc" ) ) ] -#[ cfg( not( feature = "use_alloc" ) ) ] +// qqq : zzz : remove #[ cfg( not( feature = "use_alloc" ) ) ] -- done +// #[ 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(), ] ) + .replace( collection_tools::hmap![ "a".to_string() => "x".to_string(), "b".to_string() => "y".to_string(), ] ) .form(); - let exp = hmap! + let exp = collection_tools::hmap! [ "a".to_string() => "x".to_string(), "b".to_string() => "y".to_string(), @@ -104,6 +104,48 @@ fn replace() } +#[ test ] +fn entity_to() +{ + + let got = < HashMap< i32, i32 > as former::EntityToFormer< former::HashMapDefinition< i32, i32, (), HashMap< i32, i32 >, former::ReturnStorage > > > + ::Former::new( former::ReturnStorage ) + .add( ( 13, 14 ) ) + .form(); + let exp = collection_tools::hmap![ 13 => 14 ]; + a_id!( got, exp ); + + let got = < HashMap< i32, i32 > as former::EntityToStorage >::Storage::default(); + let exp = + < + HashMap< i32, i32 > as former::EntityToFormer + < + former::HashMapDefinition + < + i32, + i32, + (), + HashMap< i32, i32 >, + former::ReturnStorage, + > + > + >::Former::new( former::ReturnStorage ) + .form(); + a_id!( got, exp ); + + let got = < HashMap< i32, i32 > as former::EntityToStorage >::Storage::default(); + let exp = + < + HashMap< i32, i32 > as former::EntityToFormer + < + < HashMap< i32, i32 > as former::EntityToDefinition< (), HashMap< i32, i32 >, former::ReturnPreformed > >::Definition + > + >::Former::new( former::ReturnPreformed ) + .form(); + a_id!( got, exp ); + +} + #[ test ] fn entry_to_val() { @@ -138,3 +180,42 @@ fn val_to_entry() a_id!( got, exp ) } + +#[ test ] +fn subformer() +{ + + /// 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_collection( definition = former::HashMapDefinition ) ] + children : HashMap< u32, Child >, + } + + let got = Parent::former() + .children() + .add( ( 0, Child::former().name( "a" ).form() ) ) + .add( ( 1, Child::former().name( "b" ).form() ) ) + .end() + .form(); + + let children = collection_tools::hmap! + [ + 0 => Child { name : "a".to_string(), data : false }, + 1 => 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/collection_former_hashset.rs b/module/core/former/tests/inc/former_tests/collection_former_hashset.rs new file mode 100644 index 0000000000..031efb7528 --- /dev/null +++ b/module/core/former/tests/inc/former_tests/collection_former_hashset.rs @@ -0,0 +1,201 @@ +#![ allow( dead_code ) ] + +#[ allow( unused_imports ) ] +use super::*; +#[ allow( unused_imports ) ] +use collection_tools::HashSet; + +// qqq : zzz : remove #[ cfg( not( feature = "use_alloc" ) ) ] -- done +// #[ cfg( not( feature = "use_alloc" ) ) ] +#[ test ] +fn add() +{ + + // explicit with CollectionFormer + + let got : HashSet< String > = the_module + ::CollectionFormer + ::< String, former::HashSetDefinition< String, (), HashSet< String >, the_module::ReturnStorage > > + ::new( former::ReturnStorage ) + .add( "a" ) + .add( "b" ) + .form(); + let exp = collection_tools::hset! + [ + "a".to_string(), + "b".to_string(), + ]; + a_id!( got, exp ); + + // explicit 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 = collection_tools::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 = collection_tools::hset! + [ + "a".to_string(), + "b".to_string(), + ]; + a_id!( got, exp ); + + // with begin_coercing + + let got : HashSet< String > = the_module::HashSetFormer + ::begin( Some( collection_tools::hset![ "a".to_string() ] ), Some( () ), former::ReturnStorage ) + .add( "b" ) + .form(); + let exp = collection_tools::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 = collection_tools::hset! + [ + "a".to_string(), + "b".to_string(), + ]; + a_id!( got, exp ); + + // + +} + +// qqq : zzz : remove #[ cfg( not( feature = "use_alloc" ) ) ] -- done +// #[ cfg( not( feature = "use_alloc" ) ) ] +#[ test ] +fn replace() +{ + + let got : HashSet< String > = the_module::HashSetFormer::new( former::ReturnStorage ) + .add( "x" ) + .replace( collection_tools::hset![ "a".to_string(), "b".to_string() ] ) + .form(); + let exp = collection_tools::hset! + [ + "a".to_string(), + "b".to_string(), + ]; + a_id!( got, exp ); + +} + +#[ test ] +fn entity_to() +{ + + let got = < HashSet< i32 > as former::EntityToFormer< former::HashSetDefinition< i32, (), HashSet< i32 >, former::ReturnStorage > > > + ::Former::new( former::ReturnStorage ) + .add( 13 ) + .form(); + let exp = collection_tools::hset![ 13 ]; + a_id!( got, exp ); + + let got = < HashSet< i32 > as former::EntityToStorage >::Storage::default(); + let exp = + < + HashSet< i32 > as former::EntityToFormer + < + former::HashSetDefinition + < + i32, + (), + HashSet< i32 >, + former::ReturnStorage, + > + > + >::Former::new( former::ReturnStorage ) + .form(); + a_id!( got, exp ); + + let got = < HashSet< i32 > as former::EntityToStorage >::Storage::default(); + let exp = + < + HashSet< i32 > as former::EntityToFormer + < + < HashSet< i32 > as former::EntityToDefinition< (), HashSet< i32 >, former::ReturnPreformed > >::Definition + > + >::Former::new( former::ReturnPreformed ) + .form(); + 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 ) +} + +#[ test ] +fn subformer() +{ + + /// Parameter description. + #[ derive( Debug, Default, PartialEq, Eq, Hash, 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_collection( definition = former::HashSetDefinition ) ] + children : HashSet< Child >, + } + + let got = Parent::former() + .children() + .add( Child::former().name( "a" ).form() ) + .add( Child::former().name( "b" ).form() ) + .end() + .form(); + + let children = collection_tools::hset! + [ + 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/collection_former_linked_list.rs b/module/core/former/tests/inc/former_tests/collection_former_linked_list.rs new file mode 100644 index 0000000000..286288f859 --- /dev/null +++ b/module/core/former/tests/inc/former_tests/collection_former_linked_list.rs @@ -0,0 +1,203 @@ +// #![ allow( dead_code ) ] + +use super::*; +#[ allow( unused_imports ) ] +use collection_tools::LinkedList; + +// + +#[ test ] +fn add() +{ + + // explicit with CollectionFormer + + let got : LinkedList< String > = the_module + ::CollectionFormer + ::< String, former::LinkedListDefinition< String, (), LinkedList< String >, the_module::ReturnStorage > > + ::new( former::ReturnStorage ) + .add( "a" ) + .add( "b" ) + .form(); + let exp = collection_tools::list! + [ + "a".to_string(), + "b".to_string(), + ]; + a_id!( got, exp ); + + // explicit with LinkedListFormer + + let got : LinkedList< String > = the_module::LinkedListFormer::< String, (), LinkedList< String >, the_module::ReturnStorage > + ::new( former::ReturnStorage ) + .add( "a" ) + .add( "b" ) + .form(); + let exp = collection_tools::list! + [ + "a".to_string(), + "b".to_string(), + ]; + a_id!( got, exp ); + + // compact with Former + + let got : LinkedList< String > = the_module::LinkedListFormer::new( former::ReturnStorage ) + .add( "a" ) + .add( "b" ) + .form(); + let exp = collection_tools::list! + [ + "a".to_string(), + "b".to_string(), + ]; + a_id!( got, exp ); + + // with begin_coercing + + let got : LinkedList< String > = the_module::LinkedListFormer + ::begin( Some( collection_tools::list![ "a".to_string() ] ), Some( () ), former::ReturnStorage ) + .add( "b" ) + .form(); + let exp = collection_tools::list! + [ + "a".to_string(), + "b".to_string(), + ]; + a_id!( got, exp ); + + // with help of ext + + use the_module::LinkedListExt; + let got : LinkedList< String > = LinkedList::former() + .add( "a" ) + .add( "b" ) + .form(); + let exp = collection_tools::list! + [ + "a".to_string(), + "b".to_string(), + ]; + a_id!( got, exp ); + + // + +} + +// + +#[ test ] +fn replace() +{ + + let got : LinkedList< String > = the_module::LinkedListFormer::new( former::ReturnStorage ) + .add( "x" ) + .replace( collection_tools::list![ "a".to_string(), "b".to_string() ] ) + .form(); + let exp = collection_tools::list! + [ + "a".to_string(), + "b".to_string(), + ]; + a_id!( got, exp ); + +} + +// + +#[ test ] +fn entity_to() +{ + + let got = < LinkedList< i32 > as former::EntityToFormer< former::LinkedListDefinition< i32, (), LinkedList< i32 >, former::ReturnPreformed > > > + ::Former::new( former::ReturnPreformed ) + .add( 13 ) + .form(); + let exp = collection_tools::list![ 13 ]; + a_id!( got, exp ); + + // qqq : uncomment and make it working + let got = < LinkedList< i32 > as former::EntityToStorage >::Storage::default(); + let exp = + < + LinkedList< i32 > as former::EntityToFormer + < + former::LinkedListDefinition + < + i32, + (), + LinkedList< i32 >, + former::ReturnPreformed, + > + > + >::Former::new( former::ReturnPreformed ) + .form(); + a_id!( got, exp ); + + let got = < LinkedList< i32 > as former::EntityToStorage >::Storage::default(); + let exp = + < + LinkedList< i32 > as former::EntityToFormer + < + < LinkedList< i32 > as former::EntityToDefinition< (), LinkedList< i32 >, former::ReturnPreformed > >::Definition + > + >::Former::new( former::ReturnPreformed ) + .form(); + a_id!( got, exp ); + +} + +#[ test ] +fn entry_to_val() +{ + let got = former::EntryToVal::< LinkedList< i32 > >::entry_to_val( 13 ); + let exp = 13i32; + a_id!( got, exp ) +} + +#[ test ] +fn val_to_entry() +{ + let got = former::ValToEntry::< LinkedList< i32 > >::val_to_entry( 13 ); + let exp = 13; + a_id!( got, exp ) +} + +#[ test ] +fn subformer() +{ + + /// 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_collection( definition = former::LinkedListDefinition ) ] + children : LinkedList< Child >, + } + + let got = Parent::former() + .children() + .add( Child::former().name( "a" ).form() ) + .add( Child::former().name( "b" ).form() ) + .end() + .form(); + + let children = collection_tools::list! + [ + 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/collection_former_vec.rs b/module/core/former/tests/inc/former_tests/collection_former_vec.rs new file mode 100644 index 0000000000..4a40e45a25 --- /dev/null +++ b/module/core/former/tests/inc/former_tests/collection_former_vec.rs @@ -0,0 +1,205 @@ +// #![ allow( dead_code ) ] + +use super::*; +#[ allow( unused_imports ) ] +use collection_tools::Vec; + +// + +#[ test ] +fn add() +{ + + // expliccit with CollectionFormer + + let got : Vec< String > = the_module + ::CollectionFormer + ::< String, former::VectorDefinition< String, (), Vec< String >, the_module::ReturnStorage > > + ::new( former::ReturnStorage ) + .add( "a" ) + .add( "b" ) + .form(); + let exp = collection_tools::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 = collection_tools::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 = collection_tools::vec! + [ + "a".to_string(), + "b".to_string(), + ]; + a_id!( got, exp ); + + // with begin_coercing + + let got : Vec< String > = the_module::VectorFormer + ::begin( Some( collection_tools::vec![ "a".to_string() ] ), Some( () ), former::ReturnStorage ) + .add( "b" ) + .form(); + let exp = collection_tools::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 = collection_tools::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( collection_tools::vec![ "a".to_string(), "b".to_string() ] ) + .form(); + let exp = collection_tools::vec! + [ + "a".to_string(), + "b".to_string(), + ]; + a_id!( got, exp ); + +} + +// + +// qqq : make similar test for all collections -- done +#[ test ] +fn entity_to() +{ + + // qqq : uncomment and make it working -- done + let got = < Vec< i32 > as former::EntityToFormer< former::VectorDefinition< i32, (), Vec< i32 >, former::ReturnPreformed > > > + ::Former::new( former::ReturnPreformed ) + .add( 13 ) + .form(); + let exp = collection_tools::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 + < + former::VectorDefinition + < + i32, + (), + Vec< i32 >, + former::ReturnPreformed, + > + > + >::Former::new( former::ReturnPreformed ) + .form(); + a_id!( got, exp ); + + 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 ) + .form(); + a_id!( got, exp ); + +} + +#[ 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 ) +} + +#[ test ] +fn subformer() +{ + + /// 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_collection( definition = former::VectorDefinition ) ] + children : Vec< Child >, + } + + let got = Parent::former() + .children() + .add( Child::former().name( "a" ).form() ) + .add( Child::former().name( "b" ).form() ) + .end() + .form(); + + let children = collection_tools::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/collection_former_vec_deque.rs b/module/core/former/tests/inc/former_tests/collection_former_vec_deque.rs new file mode 100644 index 0000000000..04c29f23b2 --- /dev/null +++ b/module/core/former/tests/inc/former_tests/collection_former_vec_deque.rs @@ -0,0 +1,205 @@ +// #![ allow( dead_code ) ] + +use super::*; +#[ allow( unused_imports ) ] +use collection_tools::VecDeque; + +// + +#[ test ] +fn add() +{ + + // explicit with CollectionFormer + + let got : VecDeque< String > = the_module + ::CollectionFormer + ::< String, former::VecDequeDefinition< String, (), VecDeque< String >, the_module::ReturnStorage > > + ::new( former::ReturnStorage ) + .add( "a" ) + .add( "b" ) + .form(); + let exp = collection_tools::vecd! + [ + "a".to_string(), + "b".to_string(), + ]; + a_id!( got, exp ); + + // explicit with VecDequeFormer + + let got : VecDeque< String > = the_module::VecDequeFormer::< String, (), VecDeque< String >, the_module::ReturnStorage > + ::new( former::ReturnStorage ) + .add( "a" ) + .add( "b" ) + .form(); + let exp = collection_tools::vecd! + [ + "a".to_string(), + "b".to_string(), + ]; + a_id!( got, exp ); + + // compact with VecDequeFormer + + let got : VecDeque< String > = the_module::VecDequeFormer::new( former::ReturnStorage ) + .add( "a" ) + .add( "b" ) + .form(); + let exp = collection_tools::vecd! + [ + "a".to_string(), + "b".to_string(), + ]; + a_id!( got, exp ); + + // with begin_coercing + + let got : VecDeque< String > = the_module::VecDequeFormer + ::begin( Some( collection_tools::vecd![ "a".to_string() ] ), Some( () ), former::ReturnStorage ) + .add( "b" ) + .form(); + let exp = collection_tools::vecd! + [ + "a".to_string(), + "b".to_string(), + ]; + a_id!( got, exp ); + + // with help of ext + + use the_module::VecDequeExt; + let got : VecDeque< String > = VecDeque::former() + .add( "a" ) + .add( "b" ) + .form(); + let exp = collection_tools::vecd! + [ + "a".to_string(), + "b".to_string(), + ]; + a_id!( got, exp ); + + // + +} + +// + +#[ test ] +fn replace() +{ + + let got : VecDeque< String > = the_module::VecDequeFormer::new( former::ReturnStorage ) + .add( "x" ) + .replace( collection_tools::vecd![ "a".to_string(), "b".to_string() ] ) + .form(); + let exp = collection_tools::vecd! + [ + "a".to_string(), + "b".to_string(), + ]; + a_id!( got, exp ); + +} + +// + +// qqq : make similar test for all collections -- done +#[ test ] +fn entity_to() +{ + + // qqq : uncomment and make it working -- done + let got = < VecDeque< i32 > as former::EntityToFormer< former::VecDequeDefinition< i32, (), VecDeque< i32 >, former::ReturnStorage > > > + ::Former::new( former::ReturnStorage ) + .add( 13 ) + .form(); + let exp = collection_tools::vecd![ 13 ]; + a_id!( got, exp ); + + // qqq : uncomment and make it working + let got = < VecDeque< i32 > as former::EntityToStorage >::Storage::default(); + let exp = + < + VecDeque< i32 > as former::EntityToFormer + < + former::VecDequeDefinition + < + i32, + (), + VecDeque< i32 >, + former::ReturnStorage, + > + > + >::Former::new( former::ReturnStorage ) + .form(); + a_id!( got, exp ); + + let got = < VecDeque< i32 > as former::EntityToStorage >::Storage::default(); + let exp = + < + VecDeque< i32 > as former::EntityToFormer + < + < VecDeque< i32 > as former::EntityToDefinition< (), VecDeque< i32 >, former::ReturnPreformed > >::Definition + > + >::Former::new( former::ReturnPreformed ) + .form(); + a_id!( got, exp ); + +} + +#[ test ] +fn entry_to_val() +{ + let got = former::EntryToVal::< VecDeque< i32 > >::entry_to_val( 13 ); + let exp = 13; + a_id!( got, exp ) +} + +#[ test ] +fn val_to_entry() +{ + let got = former::ValToEntry::< VecDeque< i32 > >::val_to_entry( 13 ); + let exp = 13; + a_id!( got, exp ) +} + +#[ test ] +fn subformer() +{ + + /// 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_collection( definition = former::VecDequeDefinition ) ] + children : VecDeque< Child >, + } + + let got = Parent::former() + .children() + .add( Child::former().name( "a" ).form() ) + .add( Child::former().name( "b" ).form() ) + .end() + .form(); + + let children = collection_tools::vecd! + [ + 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/container_former_hashset.rs b/module/core/former/tests/inc/former_tests/container_former_hashset.rs deleted file mode 100644 index 83b8e7a994..0000000000 --- a/module/core/former/tests/inc/former_tests/container_former_hashset.rs +++ /dev/null @@ -1,121 +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 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 deleted file mode 100644 index ec76210448..0000000000 --- a/module/core/former/tests/inc/former_tests/container_former_vec.rs +++ /dev/null @@ -1,158 +0,0 @@ -// #![ 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 8123626a1d..ca4b73fc02 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 @@ -3,13 +3,17 @@ #[ allow( unused_imports ) ] use super::*; + +pub mod core {} +pub mod std {} +pub mod marker {} pub trait CloneAny{} -pub trait End{} +// pub trait Context{} pub trait Formed{} pub trait OnEnd{} #[ derive( Clone, the_module::Former ) ] pub struct Context { - inner : std::sync::Arc< core::cell::RefCell< dyn CloneAny > > + inner : ::std::sync::Arc< ::core::cell::RefCell< dyn CloneAny > > } diff --git a/module/core/former/tests/inc/former_tests/name_collision_core.rs b/module/core/former/tests/inc/former_tests/name_collision_core.rs new file mode 100644 index 0000000000..8cdf38cb3f --- /dev/null +++ b/module/core/former/tests/inc/former_tests/name_collision_core.rs @@ -0,0 +1,19 @@ +#![ allow( dead_code ) ] +#![ allow( non_camel_case_types ) ] + +#[ allow( unused_imports ) ] +use super::*; + +// pub mod core {} +pub mod std {} +pub mod marker {} +pub trait CloneAny{} +pub trait Context{} +pub trait Formed{} +pub trait OnEnd{} + +#[ derive( Clone, the_module::Former ) ] +pub struct core +{ + inner : ::std::sync::Arc< ::core::cell::RefCell< dyn CloneAny > > +} 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 99f736019d..b998b6153c 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 @@ -3,6 +3,9 @@ #[ allow( unused_imports ) ] use super::*; +pub mod core {} +pub mod std {} +pub mod marker {} pub trait CloneAny{} pub trait Context{} pub trait Formed{} @@ -13,7 +16,7 @@ pub trait OnEnd{} // #[ derive( Clone ) ] pub struct End { - inner : std::sync::Arc< core::cell::RefCell< dyn CloneAny > > + inner : ::std::sync::Arc< ::core::cell::RefCell< dyn CloneAny > > } // = begin_coercing of generated diff --git a/module/core/former/tests/inc/former_tests/name_collision_former_hashmap_without_parameter.rs b/module/core/former/tests/inc/former_tests/name_collision_former_hashmap_without_parameter.rs index dd533926c6..31df1f43e6 100644 --- a/module/core/former/tests/inc/former_tests/name_collision_former_hashmap_without_parameter.rs +++ b/module/core/former/tests/inc/former_tests/name_collision_former_hashmap_without_parameter.rs @@ -1,6 +1,14 @@ use super::*; use the_module::Former; +pub mod core {} +pub mod std {} +pub mod marker {} +pub trait CloneAny{} +pub trait Context{} +pub trait Formed{} +pub trait OnEnd{} + #[ derive( Debug, PartialEq ) ] struct HashMap< T > { diff --git a/module/core/former/tests/inc/former_tests/name_collision_former_vector_without_parameter.rs b/module/core/former/tests/inc/former_tests/name_collision_former_vector_without_parameter.rs index 87f073d348..c79d0e8ba3 100644 --- a/module/core/former/tests/inc/former_tests/name_collision_former_vector_without_parameter.rs +++ b/module/core/former/tests/inc/former_tests/name_collision_former_vector_without_parameter.rs @@ -1,6 +1,14 @@ use super::*; use the_module::Former; +pub mod core {} +pub mod std {} +pub mod marker {} +pub trait CloneAny{} +pub trait Context{} +pub trait Formed{} +pub trait OnEnd{} + #[ derive( Debug, PartialEq ) ] struct Vec { diff --git a/module/core/former/tests/inc/former_tests/name_collision_on_end.rs b/module/core/former/tests/inc/former_tests/name_collision_on_end.rs index d7bf8109cd..3645d92588 100644 --- a/module/core/former/tests/inc/former_tests/name_collision_on_end.rs +++ b/module/core/former/tests/inc/former_tests/name_collision_on_end.rs @@ -3,12 +3,16 @@ #[ allow( unused_imports ) ] use super::*; +pub mod core {} +pub mod std {} +pub mod marker {} pub trait CloneAny{} pub trait Context{} -pub trait End{} +pub trait Formed{} +// pub trait OnEnd{} #[ derive( Clone, the_module::Former ) ] pub struct OnEnd { - inner : std::sync::Arc< core::cell::RefCell< dyn CloneAny > > + inner : ::std::sync::Arc< ::core::cell::RefCell< dyn CloneAny > > } 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 843af61803..e23adcdd45 100644 --- a/module/core/former/tests/inc/former_tests/name_collisions.rs +++ b/module/core/former/tests/inc/former_tests/name_collisions.rs @@ -1,6 +1,13 @@ #[ allow( unused_imports ) ] use super::*; +pub mod core {} +pub mod std {} +pub trait CloneAny{} +pub trait Context{} +pub trait Formed{} +pub trait OnEnd{} + #[ allow( dead_code ) ] type Option = (); #[ allow( dead_code ) ] @@ -28,10 +35,10 @@ type HashMap = (); pub struct Struct1 { vec_1 : Vec< String >, - hashmap_1 : std::collections::HashMap< String, String >, - hashset_1 : std::collections::HashSet< String >, + hashmap_1 : collection_tools::HashMap< String, String >, + hashset_1 : collection_tools::HashSet< String >, } // -include!( "./only_test/containers_without_subformer.rs" ); +include!( "./only_test/collections_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 index a4b4dbf907..a3a0f00e2b 100644 --- a/module/core/former/tests/inc/former_tests/only_test/basic.rs +++ b/module/core/former/tests/inc/former_tests/only_test/basic.rs @@ -16,9 +16,9 @@ tests_impls! 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 ) ); + 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 ) ); + 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 ); @@ -255,7 +255,7 @@ tests_impls! // 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 ) ); + 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 ); @@ -324,7 +324,7 @@ tests_impls! // 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 ) ); + 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 ); 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/collections_with_subformer.rs similarity index 53% rename from module/core/former/tests/inc/former_tests/only_test/containers_with_subformer.rs rename to module/core/former/tests/inc/former_tests/only_test/collections_with_subformer.rs index 042d36e538..3765e3eb92 100644 --- a/module/core/former/tests/inc/former_tests/only_test/containers_with_subformer.rs +++ b/module/core/former/tests/inc/former_tests/only_test/collections_with_subformer.rs @@ -51,24 +51,24 @@ tests_impls! 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 ) ); + 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 ) ); + 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() + .vec_1().replace( collection_tools::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!{}, + vec_1 : collection_tools::vec![ "a".to_string(), "b".to_string() ], + hashmap_1 : collection_tools::hmap!{}, + hashset_1 : collection_tools::hset!{}, }; a_id!( got, exp ); @@ -76,13 +76,13 @@ tests_impls! let got : Struct1 = Struct1Former ::< Struct1FormerDefinition< (), Struct1, _ > > ::new( | storage, _context | { former::StoragePreform::preform( storage ) } ) - .vec_1().replace( vec![ "a".to_string(), "b".to_string() ] ).end() + .vec_1().replace( collection_tools::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!{}, + vec_1 : collection_tools::vec![ "a".to_string(), "b".to_string() ], + hashmap_1 : collection_tools::hmap!{}, + hashset_1 : collection_tools::hset!{}, }; a_id!( got, exp ); @@ -90,13 +90,13 @@ tests_impls! // 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() + // .vec_1().replace( collection_tools::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!{}, + // vec_1 : collection_tools::vec![ "a".to_string(), "b".to_string() ], + // hashmap_1 : collection_tools::hmap!{}, + // hashset_1 : collection_tools::hset!{}, // }; // a_id!( got, exp ); @@ -104,13 +104,13 @@ tests_impls! 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() + .vec_1().replace( collection_tools::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!{}, + vec_1 : collection_tools::vec![ "a".to_string(), "b".to_string() ], + hashmap_1 : collection_tools::hmap!{}, + hashset_1 : collection_tools::hset!{}, }; a_id!( got, exp ); @@ -121,10 +121,10 @@ tests_impls! fn field_forming_end() { - // Container subformers are defined - let _got = Struct1FormerAssignVec1End::< Struct1FormerDefinition >::default(); - let _got = Struct1FormerAssignHashmap1End::< Struct1FormerDefinition >::default(); - let _got = Struct1FormerAssignHashset1End::< Struct1FormerDefinition >::default(); + // Collection subformers are defined + let _got = Struct1SubformCollectionVec1End::< Struct1FormerDefinition >::default(); + let _got = Struct1SubformCollectionHashmap1End::< Struct1FormerDefinition >::default(); + let _got = Struct1SubformCollectionHashset1End::< Struct1FormerDefinition >::default(); // AsSubformerEnd is defined fn _f1< End : Struct1AsSubformerEnd< Struct1Former > > @@ -152,48 +152,48 @@ tests_impls! let expected = Struct1 { - vec_1 : vec![ "ghi".to_string(), "klm".to_string() ], - hashmap_1 : hmap!{}, - hashset_1 : hset!{}, + vec_1 : collection_tools::vec![ "ghi".to_string(), "klm".to_string() ], + hashmap_1 : collection_tools::hmap!{}, + hashset_1 : collection_tools::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() + .vec_1().replace( collection_tools::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!{}, + vec_1 : collection_tools::vec![ "a".to_string(), "bc".to_string(), "def".to_string() ], + hashmap_1 : collection_tools::hmap!{}, + hashset_1 : collection_tools::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() + .vec_1().add( "x" ).replace( collection_tools::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!{}, + vec_1 : collection_tools::vec![ "a".to_string(), "bc".to_string(), "def".to_string() ], + hashmap_1 : collection_tools::hmap!{}, + hashset_1 : collection_tools::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() + .vec_1().replace( collection_tools::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!{}, + vec_1 : collection_tools::vec![ "a".to_string(), "bc".to_string(), "def".to_string(), "gh".to_string() ], + hashmap_1 : collection_tools::hmap!{}, + hashset_1 : collection_tools::hset!{}, }; a_id!( command, expected ); } @@ -213,42 +213,42 @@ tests_impls! let expected = Struct1 { - vec_1 : vec![], - hashmap_1 : hmap!{ "k1".to_string() => "v1".to_string(), "k2".to_string() => "v2".to_string() }, - hashset_1 : hset!{}, + vec_1 : collection_tools::vec![], + hashmap_1 : collection_tools::hmap!{ "k1".to_string() => "v1".to_string(), "k2".to_string() => "v2".to_string() }, + hashset_1 : collection_tools::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() + .hashmap_1().replace( collection_tools::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!{}, + vec_1 : collection_tools::vec![], + hashmap_1 : collection_tools::hmap!{ "k1".to_string() => "v1".to_string(), "k2".to_string() => "v2".to_string() }, + hashset_1 : collection_tools::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() + .hashmap_1().add( ( "x".to_string(), "v1".to_string() ) ).replace( collection_tools::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!{}, + vec_1 : collection_tools::vec![], + hashmap_1 : collection_tools::hmap!{ "k1".to_string() => "v1".to_string(), "k2".to_string() => "v2".to_string() }, + hashset_1 : collection_tools::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() } ) + .hashmap_1().replace( collection_tools::hmap!{ "k1".to_string() => "v1".to_string(), "k2".to_string() => "v2".to_string() } ) .add( ( "k3".to_string(), "v3".to_string() ) ).end() .form() ; @@ -256,9 +256,9 @@ tests_impls! 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!{}, + vec_1 : collection_tools::vec![], + hashmap_1 : collection_tools::hmap!{ "k1".to_string() => "v1".to_string(), "k2".to_string() => "v2".to_string(), "k3".to_string() => "v3".to_string() }, + hashset_1 : collection_tools::hset!{}, }; a_id!( command, expected ); } @@ -278,51 +278,51 @@ tests_impls! let expected = Struct1 { - vec_1 : vec![], - hashmap_1 : hmap!{}, - hashset_1 : hset!{ "v1".to_string(), "v2".to_string() }, + vec_1 : collection_tools::vec![], + hashmap_1 : collection_tools::hmap!{}, + hashset_1 : collection_tools::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() + .hashset_1().replace( collection_tools::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() }, + vec_1 : collection_tools::vec![], + hashmap_1 : collection_tools::hmap!{}, + hashset_1 : collection_tools::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() + .hashset_1().add( "x" ).replace( collection_tools::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() }, + vec_1 : collection_tools::vec![], + hashmap_1 : collection_tools::hmap!{}, + hashset_1 : collection_tools::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() + .hashset_1().replace( collection_tools::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() }, + vec_1 : collection_tools::vec![], + hashmap_1 : collection_tools::hmap!{}, + hashset_1 : collection_tools::hset!{ "v1".to_string(), "v2".to_string(), "v3".to_string() }, }; a_id!( command, expected ); } @@ -341,9 +341,9 @@ tests_impls! 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() }, + vec_1 : collection_tools::vec![ "ghi".to_string(), "klm".to_string() ], + hashmap_1 : collection_tools::hmap!{ "k1".to_string() => "v1".to_string(), "k2".to_string() => "v2".to_string() }, + hashset_1 : collection_tools::hset!{ "k1".to_string() }, }; a_id!( command, expected ); diff --git a/module/core/former/tests/inc/former_tests/only_test/containers_without_subformer.rs b/module/core/former/tests/inc/former_tests/only_test/collections_without_subformer.rs similarity index 54% rename from module/core/former/tests/inc/former_tests/only_test/containers_without_subformer.rs rename to module/core/former/tests/inc/former_tests/only_test/collections_without_subformer.rs index c19e92eead..4b68747c33 100644 --- a/module/core/former/tests/inc/former_tests/only_test/containers_without_subformer.rs +++ b/module/core/former/tests/inc/former_tests/only_test/collections_without_subformer.rs @@ -20,22 +20,22 @@ tests_impls! a_id!( former.context, None ); 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 ) ); + 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_1, hmap!{} ); - a_id!( command.hashset_1, hset![] ); + a_id!( command.hashmap_1, collection_tools::hmap!{} ); + a_id!( command.hashset_1, collection_tools::hset![] ); let command = Struct1::former().perform(); a_id!( command.vec_1, Vec::< String >::new() ); - a_id!( command.hashmap_1, hmap!{} ); - a_id!( command.hashset_1, hset![] ); + a_id!( command.hashmap_1, collection_tools::hmap!{} ); + a_id!( command.hashset_1, collection_tools::hset![] ); let command = Struct1::former().end(); a_id!( command.vec_1, Vec::< String >::new() ); - a_id!( command.hashmap_1, hmap!{} ); - a_id!( command.hashset_1, hset![] ); + a_id!( command.hashmap_1, collection_tools::hmap!{} ); + a_id!( command.hashset_1, collection_tools::hset![] ); } @@ -47,16 +47,16 @@ tests_impls! // test.case( "vector : construction" ); let command = Struct1::former() - .vec_1( vec![ "ghi".to_string(), "klm".to_string() ] ) + .vec_1( collection_tools::vec![ "ghi".to_string(), "klm".to_string() ] ) .form() ; // dbg!( &command ); let expected = Struct1 { - vec_1 : vec![ "ghi".to_string(), "klm".to_string() ], - hashmap_1 : hmap!{}, - hashset_1 : hset!{}, + vec_1 : collection_tools::vec![ "ghi".to_string(), "klm".to_string() ], + hashmap_1 : collection_tools::hmap!{}, + hashset_1 : collection_tools::hset!{}, }; a_id!( command, expected ); } @@ -69,16 +69,16 @@ tests_impls! // test.case( "construction" ); let command = Struct1::former() - .hashmap_1( hmap!{ "k1".to_string() => "v1".to_string(), "k2".to_string() => "v2".to_string() } ) + .hashmap_1( collection_tools::hmap!{ "k1".to_string() => "v1".to_string(), "k2".to_string() => "v2".to_string() } ) .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!{}, + vec_1 : collection_tools::vec![], + hashmap_1 : collection_tools::hmap!{ "k1".to_string() => "v1".to_string(), "k2".to_string() => "v2".to_string() }, + hashset_1 : collection_tools::hset!{}, }; a_id!( command, expected ); } @@ -90,16 +90,16 @@ tests_impls! // test.case( "construction" ); let command = Struct1::former() - .hashset_1( hset!{ "v1".to_string(), "v2".to_string() } ) + .hashset_1( collection_tools::hset!{ "v1".to_string(), "v2".to_string() } ) .form() ; // dbg!( &command ); let expected = Struct1 { - vec_1 : vec![], - hashmap_1 : hmap!{}, - hashset_1 : hset!{ "v1".to_string(), "v2".to_string() }, + vec_1 : collection_tools::vec![], + hashmap_1 : collection_tools::hmap!{}, + hashset_1 : collection_tools::hset!{ "v1".to_string(), "v2".to_string() }, }; a_id!( command, expected ); } @@ -114,9 +114,9 @@ tests_impls! let expected = Struct1 { - vec_1 : vec![], - hashmap_1 : hmap!{}, - hashset_1 : hset!{}, + vec_1 : collection_tools::vec![], + hashmap_1 : collection_tools::hmap!{}, + hashset_1 : collection_tools::hset!{}, }; a_id!( command, expected ); } @@ -126,16 +126,16 @@ tests_impls! fn test_complex() { let command = Struct1::former() - .vec_1( vec![ "ghi".to_string(), "klm".to_string() ] ) - .hashmap_1( hmap!{ "k1".to_string() => "v1".to_string(), "k2".to_string() => "v2".to_string() } ) + .vec_1( collection_tools::vec![ "ghi".to_string(), "klm".to_string() ] ) + .hashmap_1( collection_tools::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_1 : hmap!{ "k1".to_string() => "v1".to_string(), "k2".to_string() => "v2".to_string() }, - hashset_1 : hset!{}, + vec_1 : collection_tools::vec![ "ghi".to_string(), "klm".to_string() ], + hashmap_1 : collection_tools::hmap!{ "k1".to_string() => "v1".to_string(), "k2".to_string() => "v2".to_string() }, + hashset_1 : collection_tools::hset!{}, }; a_id!( command, expected ); diff --git a/module/core/former/tests/inc/former_tests/only_test/parametrized_struct.rs b/module/core/former/tests/inc/former_tests/only_test/parametrized_struct.rs index 620e42198b..e73d4d1d88 100644 --- a/module/core/former/tests/inc/former_tests/only_test/parametrized_struct.rs +++ b/module/core/former/tests/inc/former_tests/only_test/parametrized_struct.rs @@ -39,8 +39,8 @@ fn command_form() // -// qqq : zzz : remove #[ cfg( not( feature = "use_alloc" ) ) ] -#[ cfg( not( feature = "use_alloc" ) ) ] +// qqq : zzz : remove #[ cfg( not( feature = "use_alloc" ) ) ] -- done +// #[ cfg( not( feature = "use_alloc" ) ) ] #[ test ] fn command_properties() { @@ -57,7 +57,7 @@ fn command_properties() let exp = Child::< &str > { name : "a".to_string(), - properties : hmap! + properties : collection_tools::hmap! { "property1" => Property::new( "property1", 13isize ), "property2" => Property::new( "property2", 113isize ), 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/scalar_children.rs similarity index 82% rename from module/core/former/tests/inc/former_tests/only_test/subformer_scalar_children.rs rename to module/core/former/tests/inc/former_tests/only_test/scalar_children.rs index 76fe8fa988..d8b3084d8f 100644 --- a/module/core/former/tests/inc/former_tests/only_test/subformer_scalar_children.rs +++ b/module/core/former/tests/inc/former_tests/only_test/scalar_children.rs @@ -3,7 +3,7 @@ fn scalar() { - let children = vec! + let children = collection_tools::vec! [ Child { name : "a".to_string(), data : false }, Child { name : "b".to_string(), data : false }, @@ -12,7 +12,7 @@ fn scalar() .children( children ) .form(); - let children = vec! + let children = collection_tools::vec! [ Child { name : "a".to_string(), data : false }, Child { name : "b".to_string(), data : false }, 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/scalar_children3.rs similarity index 82% rename from module/core/former/tests/inc/former_tests/only_test/subformer_scalar_children3.rs rename to module/core/former/tests/inc/former_tests/only_test/scalar_children3.rs index ba2c97b459..2d0c840078 100644 --- a/module/core/former/tests/inc/former_tests/only_test/subformer_scalar_children3.rs +++ b/module/core/former/tests/inc/former_tests/only_test/scalar_children3.rs @@ -3,7 +3,7 @@ fn scalar() { - let children = vec! + let children = collection_tools::vec! [ Child { name : "a".to_string(), data : false }, Child { name : "b".to_string(), data : false }, @@ -12,7 +12,7 @@ fn scalar() .children3( children ) .form(); - let children = vec! + let children = collection_tools::vec! [ Child { name : "a".to_string(), data : false }, Child { name : "b".to_string(), data : false }, diff --git a/module/core/former/tests/inc/former_tests/only_test/subformer_basic.rs b/module/core/former/tests/inc/former_tests/only_test/subform_basic.rs similarity index 86% rename from module/core/former/tests/inc/former_tests/only_test/subformer_basic.rs rename to module/core/former/tests/inc/former_tests/only_test/subform_basic.rs index ea039d9835..e235a46b9d 100644 --- a/module/core/former/tests/inc/former_tests/only_test/subformer_basic.rs +++ b/module/core/former/tests/inc/former_tests/only_test/subform_basic.rs @@ -13,8 +13,8 @@ // ; // ca.execute( input ).unwrap(); -// qqq : for Antont : zzz : here and in all similar tests remove `#[ cfg( not( feature = "use_alloc" ) ) ]` -#[ cfg( not( feature = "use_alloc" ) ) ] +// qqq : for Anton : zzz : here and in all similar tests remove `#[ cfg( not( feature = "use_alloc" ) ) ]` -- done +// #[ cfg( not( feature = "use_alloc" ) ) ] #[ test ] fn command_with_closure() { @@ -59,8 +59,8 @@ fn command_with_closure() // -// qqq : zzz : remove #[ cfg( not( feature = "use_alloc" ) ) ] -#[ cfg( not( feature = "use_alloc" ) ) ] +// qqq : zzz : remove #[ cfg( not( feature = "use_alloc" ) ) ] -- done +// #[ cfg( not( feature = "use_alloc" ) ) ] #[ test ] fn command_properties() { @@ -77,7 +77,7 @@ fn command_properties() { name : "a".to_string(), subject : "b".to_string(), - properties : hmap! + properties : collection_tools::hmap! { "property1" => Property::new( "property1", "simple property", 13isize ), "property2" => Property::new( "property2", "simple property 3", 113isize ), @@ -100,7 +100,7 @@ fn command_properties() { name : "a".to_string(), subject : "b".to_string(), - properties : hmap! + properties : collection_tools::hmap! { "property1" => Property::new( "property1", "simple property", 13isize ), "property2" => Property::new( "property2", "simple property 3", 113isize ), @@ -113,8 +113,8 @@ fn command_properties() // -// qqq : zzz : remove #[ cfg( not( feature = "use_alloc" ) ) ] -#[ cfg( not( feature = "use_alloc" ) ) ] +// qqq : zzz : remove #[ cfg( not( feature = "use_alloc" ) ) ] -- done +// #[ cfg( not( feature = "use_alloc" ) ) ] #[ test ] fn aggregator() { @@ -130,12 +130,12 @@ fn aggregator() { name : "name1".to_string(), subject : "s".to_string(), - properties : hmap!{}, + properties : collection_tools::hmap!{}, }; let exp = Parent { parameter1 : "p1".to_string(), - commands : hmap!{ "name1" => name1 }, + commands : collection_tools::hmap!{ "name1".to_string() => name1 }, }; dbg!( &got ); dbg!( &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/subform_collection.rs similarity index 89% rename from module/core/former/tests/inc/former_tests/only_test/subformer_container.rs rename to module/core/former/tests/inc/former_tests/only_test/subform_collection.rs index ce7f0f08dd..2c32c32ba8 100644 --- a/module/core/former/tests/inc/former_tests/only_test/subformer_container.rs +++ b/module/core/former/tests/inc/former_tests/only_test/subform_collection.rs @@ -10,7 +10,7 @@ fn basic() .end() .form(); - let children = vec! + let children = collection_tools::vec! [ Child { name : "a".to_string(), data : false }, Child { name : "b".to_string(), data : false }, 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/subform_collection_children2.rs similarity index 86% rename from module/core/former/tests/inc/former_tests/only_test/subformer_container_children2.rs rename to module/core/former/tests/inc/former_tests/only_test/subform_collection_children2.rs index 46fae25331..84f0132ed7 100644 --- a/module/core/former/tests/inc/former_tests/only_test/subformer_container_children2.rs +++ b/module/core/former/tests/inc/former_tests/only_test/subform_collection_children2.rs @@ -1,6 +1,6 @@ #[ test ] -fn container() +fn collection() { let got = Parent::former() @@ -10,7 +10,7 @@ fn container() .end() .form(); - let children = vec! + let children = collection_tools::vec! [ Child { name : "a".to_string(), data : false }, Child { name : "b".to_string(), data : false }, 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/subform_entry_child.rs similarity index 87% rename from module/core/former/tests/inc/former_tests/only_test/subformer_subform_child.rs rename to module/core/former/tests/inc/former_tests/only_test/subform_entry_child.rs index f4167ee982..a94acc77a6 100644 --- a/module/core/former/tests/inc/former_tests/only_test/subformer_subform_child.rs +++ b/module/core/former/tests/inc/former_tests/only_test/subform_entry_child.rs @@ -8,7 +8,7 @@ fn child() .child( "b" ).end() .form(); - let children = vec! + let children = collection_tools::vec! [ Child { name : "a".to_string(), data : false }, Child { name : "b".to_string(), data : false }, @@ -27,7 +27,7 @@ fn _child() ._child().name( "b" ).end() .form(); - let children = vec! + let children = collection_tools::vec! [ Child { name : "a".to_string(), data : false }, Child { name : "b".to_string(), data : false }, 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/subform_entry_children2.rs similarity index 87% rename from module/core/former/tests/inc/former_tests/only_test/subformer_subform_children2.rs rename to module/core/former/tests/inc/former_tests/only_test/subform_entry_children2.rs index c869113680..e21b9d8da0 100644 --- a/module/core/former/tests/inc/former_tests/only_test/subformer_subform_children2.rs +++ b/module/core/former/tests/inc/former_tests/only_test/subform_entry_children2.rs @@ -8,7 +8,7 @@ fn subform() .children2( "b" ).end() .form(); - let children = vec! + let children = collection_tools::vec! [ Child { name : "a".to_string(), data : false }, Child { name : "b".to_string(), data : false }, diff --git a/module/core/former/tests/inc/former_tests/only_test/subform_scalar.rs b/module/core/former/tests/inc/former_tests/only_test/subform_scalar.rs new file mode 100644 index 0000000000..90ba084724 --- /dev/null +++ b/module/core/former/tests/inc/former_tests/only_test/subform_scalar.rs @@ -0,0 +1,13 @@ + +#[ test ] +fn subforme_scalar() +{ + + let got = Parent::former() + .child().name( "a" ).data( true ).end() + .form(); + + let exp = Parent { child : Child { name : "a".to_string(), data : true } }; + a_id!( got, exp ); + +} diff --git a/module/core/former/tests/inc/former_tests/string_slice.rs b/module/core/former/tests/inc/former_tests/parametrized_slice.rs similarity index 100% rename from module/core/former/tests/inc/former_tests/string_slice.rs rename to module/core/former/tests/inc/former_tests/parametrized_slice.rs diff --git a/module/core/former/tests/inc/former_tests/string_slice_manual.rs b/module/core/former/tests/inc/former_tests/parametrized_slice_manual.rs similarity index 100% rename from module/core/former/tests/inc/former_tests/string_slice_manual.rs rename to module/core/former/tests/inc/former_tests/parametrized_slice_manual.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 d216575504..8ffd7ad762 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 @@ -28,7 +28,7 @@ impl< Name > Property< Name > pub struct Child< K : core::hash::Hash + std::cmp::Eq > { pub name : String, - #[ container( definition = former::HashMapDefinition ) ] + #[ subform_collection( definition = former::HashMapDefinition ) ] pub properties : collection_tools::HashMap< K, Property< K > >, } 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 95e081b39e..efab5306c5 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 @@ -30,7 +30,7 @@ where K : core::hash::Hash + std::cmp::Eq, { pub name : String, - // #[ container( definition = former::HashMapDefinition ) ] + // #[ subform_collection( definition = former::HashMapDefinition ) ] pub properties : collection_tools::HashMap< K, Property< K > >, } @@ -318,9 +318,9 @@ where } #[ inline( always ) ] - pub fn properties( self ) -> former::ContainerFormer::< ( K, Property< K >, ), former::HashMapDefinition< K, Property< K >, Self, Self, ChildFormerPropertiesEnd > > + pub fn properties( self ) -> former::CollectionFormer::< ( 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 > >>() + self.properties_set::< former::CollectionFormer::< ( K, Property< K >, ), former::HashMapDefinition< K, Property< K >, Self, Self, ChildFormerPropertiesEnd > >>() } } @@ -356,7 +356,7 @@ where let mut super_former = super_former.unwrap(); if let Some( ref mut field ) = super_former.storage.properties { - former::ContainerAssign::assign( field, storage ); + former::CollectionAssign::assign( field, storage ); } else { 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 063e58be1c..b45429c63c 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 @@ -30,7 +30,7 @@ where K : core::hash::Hash + std::cmp::Eq, { pub name : String, - #[ container( definition = former::HashMapDefinition ) ] + #[ subform_collection( definition = former::HashMapDefinition ) ] pub properties : collection_tools::HashMap< K, Property< K > >, } diff --git a/module/core/former/tests/inc/former_tests/subformer_subform_and_container.rs b/module/core/former/tests/inc/former_tests/subform_all.rs similarity index 79% rename from module/core/former/tests/inc/former_tests/subformer_subform_and_container.rs rename to module/core/former/tests/inc/former_tests/subform_all.rs index 6a3546113e..6a4cd78a03 100644 --- a/module/core/former/tests/inc/former_tests/subformer_subform_and_container.rs +++ b/module/core/former/tests/inc/former_tests/subform_all.rs @@ -16,9 +16,9 @@ pub struct Child // #[ derive( Debug, Default, PartialEq ) ] pub struct Parent { - #[ subform( name = _child ) ] - #[ container( name = children2 ) ] #[ scalar( name = children3 ) ] + #[ subform_collection( name = children2 ) ] + #[ subform_entry( name = _child ) ] children : Vec< Child >, } @@ -31,7 +31,7 @@ where pub fn child( self, name : &str ) -> ChildAsSubformer< Self, impl ChildAsSubformerEnd< Self > > { - self._children_add + self._children_subform_entry ::< ChildFormer< _ >, _, >() .name( name ) } @@ -51,6 +51,6 @@ where // == end of generated -include!( "./only_test/subformer_subform_child.rs" ); -include!( "./only_test/subformer_container_children2.rs" ); -include!( "./only_test/subformer_scalar_children3.rs" ); +include!( "./only_test/subform_entry_child.rs" ); +include!( "./only_test/subform_collection_children2.rs" ); +include!( "./only_test/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/subform_all_parametrized.rs similarity index 83% rename from module/core/former/tests/inc/former_tests/subformer_subform_and_container_parametrized.rs rename to module/core/former/tests/inc/former_tests/subform_all_parametrized.rs index 43347fc9ce..8d85935a66 100644 --- a/module/core/former/tests/inc/former_tests/subformer_subform_and_container_parametrized.rs +++ b/module/core/former/tests/inc/former_tests/subform_all_parametrized.rs @@ -20,9 +20,9 @@ where // #[ derive( Debug, Default, PartialEq ) ] pub struct Parent< 'child > { - #[ subform( name = _child ) ] - #[ container( name = children2 ) ] #[ scalar( name = children3 ) ] + #[ subform_collection( name = children2 ) ] + #[ subform_entry( name = _child ) ] children : Vec< Child< 'child, str > >, } @@ -35,7 +35,7 @@ where pub fn child( self, name : &str ) -> ChildAsSubformer< 'child, str, Self, impl ChildAsSubformerEnd< 'child, str, Self > > { - self._children_add + self._children_subform_entry ::< ChildFormer< '_, _, _ >, _, >() .name( name ) } @@ -55,7 +55,7 @@ fn subform_child() .child( "b" ).data( "bb" ).end() .form(); - let children = vec! + let children = collection_tools::vec! [ Child { name : "a".to_string(), data : "aa" }, Child { name : "b".to_string(), data : "bb" }, @@ -74,7 +74,7 @@ fn subform_child_generated() ._child().name( "b" ).data( "bb" ).end() .form(); - let children = vec! + let children = collection_tools::vec! [ Child { name : "a".to_string(), data : "aa" }, Child { name : "b".to_string(), data : "bb" }, @@ -85,7 +85,7 @@ fn subform_child_generated() } #[ test ] -fn container() +fn collection() { let got = Parent::former() @@ -95,7 +95,7 @@ fn container() .end() .form(); - let children = vec! + let children = collection_tools::vec! [ Child { name : "a".to_string(), data : "aa" }, Child { name : "b".to_string(), data : "bb" }, @@ -110,7 +110,7 @@ fn container() fn scalar() { - let children = vec! + let children = collection_tools::vec! [ Child { name : "a".to_string(), data : "aa" }, Child { name : "b".to_string(), data : "bb" }, @@ -119,7 +119,7 @@ fn scalar() .children3( children ) .form(); - let children = vec! + let children = collection_tools::vec! [ Child { name : "a".to_string(), data : "aa" }, Child { name : "b".to_string(), data : "bb" }, @@ -129,6 +129,6 @@ fn scalar() } -// include!( "./only_test/subformer_subform_child.rs" ); -// include!( "./only_test/subformer_container_children2.rs" ); -// include!( "./only_test/subformer_scalar_children3.rs" ); +// include!( "./only_test/subform_entry_child.rs" ); +// include!( "./only_test/subform_collection_children2.rs" ); +// include!( "./only_test/subform_scalar_children3.rs" ); diff --git a/module/core/former/tests/inc/former_tests/subformer_subform_and_container_private.rs b/module/core/former/tests/inc/former_tests/subform_all_private.rs similarity index 79% rename from module/core/former/tests/inc/former_tests/subformer_subform_and_container_private.rs rename to module/core/former/tests/inc/former_tests/subform_all_private.rs index 1c915adce1..df7f1e4738 100644 --- a/module/core/former/tests/inc/former_tests/subformer_subform_and_container_private.rs +++ b/module/core/former/tests/inc/former_tests/subform_all_private.rs @@ -16,9 +16,9 @@ struct Child // #[ derive( Debug, Default, PartialEq ) ] struct Parent { - #[ subform( name = _child ) ] - #[ container( name = children2 ) ] #[ scalar( name = children3 ) ] + #[ subform_collection( name = children2 ) ] + #[ subform_entry( name = _child ) ] children : Vec< Child >, } @@ -31,7 +31,7 @@ where fn child( self, name : &str ) -> ChildAsSubformer< Self, impl ChildAsSubformerEnd< Self > > { - self._children_add + self._children_subform_entry ::< ChildFormer< _ >, _, >() .name( name ) } @@ -51,6 +51,6 @@ where // == end of generated -include!( "./only_test/subformer_subform_child.rs" ); -include!( "./only_test/subformer_container_children2.rs" ); -include!( "./only_test/subformer_scalar_children3.rs" ); +include!( "./only_test/subform_entry_child.rs" ); +include!( "./only_test/subform_collection_children2.rs" ); +include!( "./only_test/scalar_children3.rs" ); diff --git a/module/core/former/tests/inc/former_tests/subformer_container.rs b/module/core/former/tests/inc/former_tests/subform_collection.rs similarity index 81% rename from module/core/former/tests/inc/former_tests/subformer_container.rs rename to module/core/former/tests/inc/former_tests/subform_collection.rs index c168f83e8b..782cc7f213 100644 --- a/module/core/former/tests/inc/former_tests/subformer_container.rs +++ b/module/core/former/tests/inc/former_tests/subform_collection.rs @@ -16,7 +16,7 @@ pub struct Child // #[ derive( Debug, Default, PartialEq ) ] pub struct Parent { - #[ container( definition = former::VectorDefinition ) ] + #[ subform_collection( definition = former::VectorDefinition ) ] children : Vec< Child >, } @@ -24,4 +24,4 @@ pub struct Parent // == end of generated -include!( "./only_test/subformer_container.rs" ); +include!( "./only_test/subform_collection.rs" ); diff --git a/module/core/former/tests/inc/former_tests/a_containers.rs b/module/core/former/tests/inc/former_tests/subform_collection_basic.rs similarity index 52% rename from module/core/former/tests/inc/former_tests/a_containers.rs rename to module/core/former/tests/inc/former_tests/subform_collection_basic.rs index c2466841bf..5ea8dc2e47 100644 --- a/module/core/former/tests/inc/former_tests/a_containers.rs +++ b/module/core/former/tests/inc/former_tests/subform_collection_basic.rs @@ -11,16 +11,16 @@ use super::*; // #[ derive( Default, Debug, PartialEq ) ] pub struct Struct1 { - #[ container( definition = former::VectorDefinition ) ] + #[ subform_collection( 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 >, + #[ subform_collection( definition = former::HashMapDefinition ) ] + hashmap_1 : collection_tools::HashMap< String, String >, + #[ subform_collection( definition = former::HashSetDefinition ) ] + hashset_1 : collection_tools::HashSet< String >, } // == generated begin // == generated end -include!( "./only_test/containers_with_subformer.rs" ); +include!( "./only_test/collections_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/subform_collection_basic_manual.rs similarity index 77% rename from module/core/former/tests/inc/former_tests/a_containers_manual.rs rename to module/core/former/tests/inc/former_tests/subform_collection_basic_manual.rs index eed2cbfa9a..314bace671 100644 --- a/module/core/former/tests/inc/former_tests/a_containers_manual.rs +++ b/module/core/former/tests/inc/former_tests/subform_collection_basic_manual.rs @@ -16,8 +16,6 @@ impl< > Struct1< > where { - - #[ inline( always ) ] pub fn former() -> Struct1Former< Struct1FormerDefinition<(), Struct1<>, former::ReturnPreformed> @@ -343,46 +341,46 @@ where where Former2 : former::FormerBegin < - former::VectorDefinition< String, Self, Self, Struct1FormerAssignVec1End< Definition > >, + former::VectorDefinition< String, Self, Self, Struct1SubformCollectionVec1End< Definition > >, >, - former::VectorDefinition< String, Self, Self, Struct1FormerAssignVec1End< Definition > > : former::FormerDefinition + former::VectorDefinition< String, Self, Self, Struct1SubformCollectionVec1End< Definition > > : former::FormerDefinition < - // Storage : former::ContainerAdd< Entry = < collection_tools::Vec< String > as former::Container >::Entry >, + // Storage : former::CollectionAdd< Entry = < collection_tools::Vec< String > as former::Collection >::Entry >, Storage = Vec< String >, Context = Struct1Former< Definition >, - End = Struct1FormerAssignVec1End< Definition >, + End = Struct1SubformCollectionVec1End< Definition >, >, - Struct1FormerAssignVec1End< Definition > : former::FormingEnd + Struct1SubformCollectionVec1End< Definition > : former::FormingEnd < < collection_tools::Vec< String > as former::EntityToDefinitionTypes< Self, Self > >::Types >, { - Former2::former_begin( None, Some( self ), Struct1FormerAssignVec1End::< Definition >::default() ) + Former2::former_begin( None, Some( self ), Struct1SubformCollectionVec1End::< Definition >::default() ) } #[ inline( always ) ] - pub fn vec_1( self ) -> former::ContainerFormer:: + pub fn vec_1( self ) -> former::CollectionFormer:: < String, - former::VectorDefinition< String, Self, Self, Struct1FormerAssignVec1End< Definition > >, + former::VectorDefinition< String, Self, Self, Struct1SubformCollectionVec1End< Definition > >, > where - former::VectorDefinition< String, Self, Self, Struct1FormerAssignVec1End< Definition > > : former::FormerDefinition + former::VectorDefinition< String, Self, Self, Struct1SubformCollectionVec1End< Definition > > : former::FormerDefinition < - // Storage : former::ContainerAdd< Entry = < collection_tools::Vec< String > as former::Container >::Entry >, + // Storage : former::CollectionAdd< Entry = < collection_tools::Vec< String > as former::Collection >::Entry >, Storage = Vec< String >, Context = Struct1Former< Definition >, - End = Struct1FormerAssignVec1End< Definition >, + End = Struct1SubformCollectionVec1End< Definition >, >, - Struct1FormerAssignVec1End< Definition > : former::FormingEnd + Struct1SubformCollectionVec1End< Definition > : former::FormingEnd < < collection_tools::Vec< String > as former::EntityToDefinitionTypes< Self, Self > >::Types >, { - self._vec_1_assign::< former::ContainerFormer:: + self._vec_1_assign::< former::CollectionFormer:: < String, - former::VectorDefinition< String, Self, Self, Struct1FormerAssignVec1End< Definition > >, + former::VectorDefinition< String, Self, Self, Struct1SubformCollectionVec1End< Definition > >, > > () } @@ -391,46 +389,46 @@ where where Former2 : former::FormerBegin < - former::HashMapDefinition< String, String, Self, Self, Struct1FormerAssignHashmap1End< Definition > >, + former::HashMapDefinition< String, String, Self, Self, Struct1SubformCollectionHashmap1End< Definition > >, >, - former::HashMapDefinition< String, String, Self, Self, Struct1FormerAssignHashmap1End< Definition > > : former::FormerDefinition + former::HashMapDefinition< String, String, Self, Self, Struct1SubformCollectionHashmap1End< Definition > > : former::FormerDefinition < - // Storage : former::ContainerAdd< Entry = < collection_tools::HashMap< String, String > as former::Container >::Entry >, + // Storage : former::CollectionAdd< Entry = < collection_tools::HashMap< String, String > as former::Collection >::Entry >, Storage = collection_tools::HashMap< String, String >, Context = Struct1Former< Definition >, - End = Struct1FormerAssignHashmap1End< Definition >, + End = Struct1SubformCollectionHashmap1End< Definition >, >, - Struct1FormerAssignHashmap1End< Definition > : former::FormingEnd + Struct1SubformCollectionHashmap1End< Definition > : former::FormingEnd < < collection_tools::HashMap< String, String > as former::EntityToDefinitionTypes< Self, Self > >::Types >, { - Former2::former_begin( None, Some( self ), Struct1FormerAssignHashmap1End::< Definition >::default() ) + Former2::former_begin( None, Some( self ), Struct1SubformCollectionHashmap1End::< Definition >::default() ) } #[ inline( always ) ] - pub fn hashmap_1( self ) -> former::ContainerFormer:: + pub fn hashmap_1( self ) -> former::CollectionFormer:: < ( String, String ), - former::HashMapDefinition< String, String, Self, Self, Struct1FormerAssignHashmap1End< Definition > >, + former::HashMapDefinition< String, String, Self, Self, Struct1SubformCollectionHashmap1End< Definition > >, > where - former::HashMapDefinition< String, String, Self, Self, Struct1FormerAssignHashmap1End< Definition > > : former::FormerDefinition + former::HashMapDefinition< String, String, Self, Self, Struct1SubformCollectionHashmap1End< Definition > > : former::FormerDefinition < - // Storage : former::ContainerAdd< Entry = < collection_tools::HashMap< String, String > as former::Container >::Entry >, + // Storage : former::CollectionAdd< Entry = < collection_tools::HashMap< String, String > as former::Collection >::Entry >, Storage = collection_tools::HashMap< String, String >, Context = Struct1Former< Definition >, - End = Struct1FormerAssignHashmap1End< Definition >, + End = Struct1SubformCollectionHashmap1End< Definition >, >, - Struct1FormerAssignHashmap1End< Definition > : former::FormingEnd + Struct1SubformCollectionHashmap1End< Definition > : former::FormingEnd < < collection_tools::HashMap< String, String > as former::EntityToDefinitionTypes< Self, Self > >::Types >, { - self._hashmap_1_assign::< former::ContainerFormer:: + self._hashmap_1_assign::< former::CollectionFormer:: < ( String, String ), - former::HashMapDefinition< String, String, Self, Self, Struct1FormerAssignHashmap1End< Definition > >, + former::HashMapDefinition< String, String, Self, Self, Struct1SubformCollectionHashmap1End< Definition > >, > > () } @@ -439,46 +437,46 @@ where where Former2 : former::FormerBegin < - former::HashSetDefinition< String, Self, Self, Struct1FormerAssignHashset1End< Definition > >, + former::HashSetDefinition< String, Self, Self, Struct1SubformCollectionHashset1End< Definition > >, >, - former::HashSetDefinition< String, Self, Self, Struct1FormerAssignHashset1End< Definition > > : former::FormerDefinition + former::HashSetDefinition< String, Self, Self, Struct1SubformCollectionHashset1End< Definition > > : former::FormerDefinition < - // Storage : former::ContainerAdd< Entry = < collection_tools::HashSet< String > as former::Container >::Entry >, + // Storage : former::CollectionAdd< Entry = < collection_tools::HashSet< String > as former::Collection >::Entry >, Storage = collection_tools::HashSet< String >, Context = Struct1Former< Definition >, - End = Struct1FormerAssignHashset1End< Definition >, + End = Struct1SubformCollectionHashset1End< Definition >, >, - Struct1FormerAssignHashset1End< Definition > : former::FormingEnd + Struct1SubformCollectionHashset1End< Definition > : former::FormingEnd < < collection_tools::HashSet< String > as former::EntityToDefinitionTypes< Self, Self > >::Types >, { - Former2::former_begin( None, Some( self ), Struct1FormerAssignHashset1End::< Definition >::default() ) + Former2::former_begin( None, Some( self ), Struct1SubformCollectionHashset1End::< Definition >::default() ) } #[ inline( always ) ] - pub fn hashset_1( self ) -> former::ContainerFormer:: + pub fn hashset_1( self ) -> former::CollectionFormer:: < String, - former::HashSetDefinition< String, Self, Self, Struct1FormerAssignHashset1End< Definition > >, + former::HashSetDefinition< String, Self, Self, Struct1SubformCollectionHashset1End< Definition > >, > where - former::HashSetDefinition< String, Self, Self, Struct1FormerAssignHashset1End< Definition > > : former::FormerDefinition + former::HashSetDefinition< String, Self, Self, Struct1SubformCollectionHashset1End< Definition > > : former::FormerDefinition < - // Storage : former::ContainerAdd< Entry = < collection_tools::HashSet< String > as former::Container >::Entry >, + // Storage : former::CollectionAdd< Entry = < collection_tools::HashSet< String > as former::Collection >::Entry >, Storage = collection_tools::HashSet< String >, Context = Struct1Former< Definition >, - End = Struct1FormerAssignHashset1End< Definition >, + End = Struct1SubformCollectionHashset1End< Definition >, >, - Struct1FormerAssignHashset1End< Definition > : former::FormingEnd + Struct1SubformCollectionHashset1End< Definition > : former::FormingEnd < < collection_tools::HashSet< String > as former::EntityToDefinitionTypes< Self, Self > >::Types >, { - self._hashset_1_assign::< former::ContainerFormer:: + self._hashset_1_assign::< former::CollectionFormer:: < String, - former::HashSetDefinition< String, Self, Self, Struct1FormerAssignHashset1End< Definition > >, + former::HashSetDefinition< String, Self, Self, Struct1SubformCollectionHashset1End< Definition > >, > > () } @@ -540,12 +538,12 @@ where // = former assign end -pub struct Struct1FormerAssignVec1End< Definition > +pub struct Struct1SubformCollectionVec1End< Definition > { _phantom : core::marker::PhantomData< ( Definition, ) >, } -impl Default for Struct1FormerAssignVec1End< Definition > +impl Default for Struct1SubformCollectionVec1End< Definition > { #[ inline( always ) ] fn default() -> Self @@ -563,7 +561,7 @@ impl< Definition > former::FormingEnd < former::VectorDefinitionTypes< String, Struct1Former< Definition >, Struct1Former< Definition > > > -for Struct1FormerAssignVec1End< Definition > +for Struct1SubformCollectionVec1End< Definition > where Definition : former::FormerDefinition< Storage = Struct1FormerStorage >, Definition::Types : former::FormerDefinitionTypes< Storage = Struct1FormerStorage >, @@ -575,7 +573,7 @@ where let mut super_former = super_former.unwrap(); if let Some( ref mut field ) = super_former.storage.vec_1 { - former::ContainerAssign::assign( field, storage ); + former::CollectionAssign::assign( field, storage ); } else { @@ -585,12 +583,12 @@ where } } -pub struct Struct1FormerAssignHashmap1End +pub struct Struct1SubformCollectionHashmap1End { _phantom : core::marker::PhantomData<(Definition,)>, } -impl Default for Struct1FormerAssignHashmap1End +impl Default for Struct1SubformCollectionHashmap1End { #[ inline( always ) ] fn default() -> Self @@ -604,7 +602,7 @@ impl Default for Struct1FormerAssignHashmap1End impl< Definition, > former::FormingEnd < former::HashMapDefinitionTypes< String, String, Struct1Former< Definition >, Struct1Former< Definition > > > -for Struct1FormerAssignHashmap1End< Definition > +for Struct1SubformCollectionHashmap1End< Definition > where Definition : former::FormerDefinition< Storage = Struct1FormerStorage >, Definition::Types : former::FormerDefinitionTypes< Storage = Struct1FormerStorage >, @@ -616,7 +614,7 @@ where let mut super_former = super_former.unwrap(); if let Some( ref mut field ) = super_former.storage.hashmap_1 { - former::ContainerAssign::assign( field, storage ); + former::CollectionAssign::assign( field, storage ); } else { @@ -626,12 +624,12 @@ where } } -pub struct Struct1FormerAssignHashset1End +pub struct Struct1SubformCollectionHashset1End { _phantom : core::marker::PhantomData<(Definition,)>, } -impl Default for Struct1FormerAssignHashset1End +impl Default for Struct1SubformCollectionHashset1End { #[ inline( always ) ] fn default() -> Self @@ -645,7 +643,7 @@ impl Default for Struct1FormerAssignHashset1End impl< Definition, > former::FormingEnd < former::HashSetDefinitionTypes< String, Struct1Former< Definition >, Struct1Former< Definition > > > -for Struct1FormerAssignHashset1End< Definition > +for Struct1SubformCollectionHashset1End< Definition > where Definition : former::FormerDefinition< Storage = Struct1FormerStorage >, Definition::Types : former::FormerDefinitionTypes< Storage = Struct1FormerStorage >, @@ -657,7 +655,7 @@ where let mut super_former = super_former.unwrap(); if let Some( ref mut field ) = super_former.storage.hashset_1 { - former::ContainerAssign::assign( field, storage ); + former::CollectionAssign::assign( field, storage ); } else { @@ -669,4 +667,4 @@ where // == end of generated -include!( "./only_test/containers_with_subformer.rs" ); +include!( "./only_test/collections_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/subform_collection_basic_scalar.rs similarity index 76% rename from module/core/former/tests/inc/former_tests/a_containers_scalar.rs rename to module/core/former/tests/inc/former_tests/subform_collection_basic_scalar.rs index 4b3efafb88..cf35e3dc49 100644 --- a/module/core/former/tests/inc/former_tests/a_containers_scalar.rs +++ b/module/core/former/tests/inc/former_tests/subform_collection_basic_scalar.rs @@ -3,8 +3,8 @@ #[ allow( unused_imports ) ] use super::*; -use std::collections::HashMap; -use std::collections::HashSet; +use collection_tools::HashMap; +use collection_tools::HashSet; #[ derive( Debug, PartialEq, the_module::Former ) ] // #[ derive( Debug, PartialEq, the_module::Former ) ] #[ debug ] @@ -20,4 +20,4 @@ pub struct Struct1 // == end of generated -include!( "./only_test/containers_without_subformer.rs" ); +include!( "./only_test/collections_without_subformer.rs" ); diff --git a/module/core/former/tests/inc/former_tests/subformer_container_custom.rs b/module/core/former/tests/inc/former_tests/subform_collection_custom.rs similarity index 90% rename from module/core/former/tests/inc/former_tests/subformer_container_custom.rs rename to module/core/former/tests/inc/former_tests/subform_collection_custom.rs index 58f3a72356..c5a4f910eb 100644 --- a/module/core/former/tests/inc/former_tests/subformer_container_custom.rs +++ b/module/core/former/tests/inc/former_tests/subform_collection_custom.rs @@ -4,9 +4,9 @@ use super::*; use collection_tools::HashSet; -// == define custom containers +// == define custom collections -// Custom container that logs additions +// Custom collection that logs additions #[ derive( Debug, PartialEq ) ] pub struct LoggingSet< K > where @@ -36,7 +36,7 @@ where K : std::cmp::Eq + std::hash::Hash, { type Item = K; - type IntoIter = std::collections::hash_set::IntoIter< K >; + type IntoIter = collection_tools::hset::IntoIter< K >; fn into_iter( self ) -> Self::IntoIter { @@ -49,7 +49,7 @@ where K : std::cmp::Eq + std::hash::Hash, { type Item = &'a K; - type IntoIter = std::collections::hash_set::Iter< 'a, K >; + type IntoIter = collection_tools::hset::Iter< 'a, K >; fn into_iter( self ) -> Self::IntoIter { @@ -57,7 +57,7 @@ where } } -impl< K > former::Container for LoggingSet< K > +impl< K > former::Collection for LoggingSet< K > where K : core::cmp::Eq + core::hash::Hash, { @@ -72,7 +72,7 @@ where } -impl< K > former::ContainerAdd for LoggingSet< K > +impl< K > former::CollectionAdd for LoggingSet< K > where K : core::cmp::Eq + core::hash::Hash, { @@ -85,7 +85,7 @@ where } -impl< K > former::ContainerAssign for LoggingSet< K > +impl< K > former::CollectionAssign for LoggingSet< K > where K : core::cmp::Eq + core::hash::Hash, { @@ -99,7 +99,7 @@ where } } -impl< K > former::ContainerValToEntry< K > for LoggingSet< K > +impl< K > former::CollectionValToEntry< K > for LoggingSet< K > where K : core::cmp::Eq + core::hash::Hash, { @@ -230,15 +230,15 @@ where // = subformer pub type LoggingSetAsSubformer< K, Context, Formed, End > = -former::ContainerFormer::< K, LoggingSetDefinition< K, Context, Formed, End > >; +former::CollectionFormer::< K, LoggingSetDefinition< K, Context, Formed, End > >; -// == use custom container +// == use custom collection /// Parent required for the template. #[ derive( Debug, Default, PartialEq, the_module::Former ) ] pub struct Parent { - #[ container ] + #[ subform_collection ] children : LoggingSet< i32 >, } diff --git a/module/core/former/tests/inc/former_tests/subformer_container_implicit.rs b/module/core/former/tests/inc/former_tests/subform_collection_implicit.rs similarity index 78% rename from module/core/former/tests/inc/former_tests/subformer_container_implicit.rs rename to module/core/former/tests/inc/former_tests/subform_collection_implicit.rs index 55b91e69de..101e5cd210 100644 --- a/module/core/former/tests/inc/former_tests/subformer_container_implicit.rs +++ b/module/core/former/tests/inc/former_tests/subform_collection_implicit.rs @@ -17,8 +17,8 @@ pub struct Child // #[ derive( Debug, Default, PartialEq ) ] pub struct Parent { - // #[ container( definition = former::VectorDefinition ) ] - #[ container ] + // #[ subform_collection( definition = former::VectorDefinition ) ] + #[ subform_collection ] children : Vec< Child >, } @@ -26,4 +26,4 @@ pub struct Parent // == end of generated -include!( "./only_test/subformer_container.rs" ); +include!( "./only_test/subform_collection.rs" ); diff --git a/module/core/former/tests/inc/former_tests/subformer_container_manual.rs b/module/core/former/tests/inc/former_tests/subform_collection_manual.rs similarity index 57% rename from module/core/former/tests/inc/former_tests/subformer_container_manual.rs rename to module/core/former/tests/inc/former_tests/subform_collection_manual.rs index 82811c38eb..ee30f941b8 100644 --- a/module/core/former/tests/inc/former_tests/subformer_container_manual.rs +++ b/module/core/former/tests/inc/former_tests/subform_collection_manual.rs @@ -16,12 +16,12 @@ pub struct Child // #[ derive( Debug, Default, PartialEq ) ] pub struct Parent { - // #[ container( definition = former::VectorDefinition ) ] + // #[ subform_collection( definition = former::VectorDefinition ) ] #[ scalar( setter = false ) ] children : Vec< Child >, } -// == begin of generated for Parent in context of attribute container( former::VectorDefinition ) ] +// == begin of generated for Parent in context of attribute collection( former::VectorDefinition ) ] #[ automatically_derived ] impl< Definition, > ParentFormer< Definition, > @@ -30,34 +30,34 @@ where { #[ inline( always ) ] - pub fn _children_container_former< Former2 >( self ) -> Former2 + pub fn _children_subform_collection< Former2 >( self ) -> Former2 where - Former2 : former::FormerBegin< former::VectorDefinition< Child, Self, Self, ParentFormerAssignChildrenEnd< Definition >, > >, + Former2 : former::FormerBegin< former::VectorDefinition< Child, Self, Self, ParentSubformCollectionChildrenEnd< Definition >, > >, { - Former2::former_begin( None, Some( self ), ParentFormerAssignChildrenEnd::< Definition >::default() ) + Former2::former_begin( None, Some( self ), ParentSubformCollectionChildrenEnd::< Definition >::default() ) } #[ inline( always ) ] - pub fn children( self ) -> former::ContainerFormer:: + pub fn children( self ) -> former::CollectionFormer:: < Child, - former::VectorDefinition< Child, Self, Self, ParentFormerAssignChildrenEnd< Definition >, > + former::VectorDefinition< Child, Self, Self, ParentSubformCollectionChildrenEnd< Definition >, > > { - self._children_container_former::< former::ContainerFormer::< Child, former::VectorDefinition< Child, Self, Self, ParentFormerAssignChildrenEnd< Definition >, > > >() + self._children_subform_collection::< former::CollectionFormer::< Child, former::VectorDefinition< Child, Self, Self, ParentSubformCollectionChildrenEnd< 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 > +#[ doc = r"Callback to return original former after forming of collection for `vec_1` is done. Callback replace content of collection assigning new content from subformer's storage." ] +pub struct ParentSubformCollectionChildrenEnd< Definition > { _phantom : core::marker::PhantomData< ( Definition, ) >, } -impl< Definition > Default for ParentFormerAssignChildrenEnd< Definition > +impl< Definition > Default for ParentSubformCollectionChildrenEnd< Definition > { #[ inline( always ) ] @@ -78,7 +78,7 @@ impl< Definition, > former::FormingEnd Vec< Child > as former::EntityToDefinitionTypes< ParentFormer< Definition, >, ParentFormer< Definition, > > >::Types > -for ParentFormerAssignChildrenEnd< Definition > +for ParentSubformCollectionChildrenEnd< Definition > where Definition : former::FormerDefinition< Storage = ParentFormerStorage< > >, { @@ -94,7 +94,7 @@ where let mut super_former = super_former.unwrap(); if let Some( ref mut field ) = super_former.storage.children { - former::ContainerAssign::assign( field, storage ); + former::CollectionAssign::assign( field, storage ); } else { @@ -104,6 +104,6 @@ where } } -// == end of generated for Parent in context of attribute container( former::VectorDefinition ) ] +// == end of generated for Parent in context of attribute collection( former::VectorDefinition ) ] -include!( "./only_test/subformer_container.rs" ); +include!( "./only_test/subform_collection.rs" ); diff --git a/module/core/former/tests/inc/former_tests/subformer_container_named.rs b/module/core/former/tests/inc/former_tests/subform_collection_named.rs similarity index 86% rename from module/core/former/tests/inc/former_tests/subformer_container_named.rs rename to module/core/former/tests/inc/former_tests/subform_collection_named.rs index 75ce8845b6..1f06c4b6ea 100644 --- a/module/core/former/tests/inc/former_tests/subformer_container_named.rs +++ b/module/core/former/tests/inc/former_tests/subform_collection_named.rs @@ -16,7 +16,7 @@ pub struct Child // #[ derive( Debug, Default, PartialEq ) ] pub struct Parent { - #[ container( name = children2 ) ] + #[ subform_collection( name = children2 ) ] children : Vec< Child >, } @@ -29,7 +29,7 @@ where pub fn children( self ) -> &'static str { r#" - Scalar setter `children` should not be generated by default if container is used. + Scalar setter `children` should not be generated by default if collection is used. It can only be generated if req "# } @@ -40,4 +40,4 @@ where // == end of generated -include!( "./only_test/subformer_container_children2.rs" ); +include!( "./only_test/subform_collection_children2.rs" ); diff --git a/module/core/former/tests/inc/former_tests/subformer_basic.rs b/module/core/former/tests/inc/former_tests/subform_collection_playground.rs similarity index 89% rename from module/core/former/tests/inc/former_tests/subformer_basic.rs rename to module/core/former/tests/inc/former_tests/subform_collection_playground.rs index 11f5a65779..0e3782a900 100644 --- a/module/core/former/tests/inc/former_tests/subformer_basic.rs +++ b/module/core/former/tests/inc/former_tests/subform_collection_playground.rs @@ -31,6 +31,7 @@ pub struct Property< Name > code : isize, } +// zzz : implement derive new /// generated by new impl< Name > Property< Name > { @@ -54,7 +55,7 @@ where { pub name : String, pub subject : String, - #[ container( definition = former::HashMapDefinition ) ] + #[ subform_collection( definition = former::HashMapDefinition ) ] pub properties : collection_tools::HashMap< K, Property< K > >, } @@ -66,7 +67,7 @@ where Definition::Storage : former::StoragePreform, { - /// Inserts a key-value pair into the map. Make a new container if it was not made so far. + /// Inserts a key-value pair into the map. Make a new collection if it was not made so far. #[ inline( always ) ] pub fn property< Name, Description, Code > ( mut self, name : Name, description : Description, code : Code ) -> Self @@ -102,10 +103,10 @@ where K : core::hash::Hash + std::cmp::Eq, { pub parameter1 : String, - #[ container( definition = former::HashMapDefinition ) ] + #[ subform_collection( definition = former::HashMapDefinition ) ] pub commands : collection_tools::HashMap< String, Child< K > >, } // == -include!( "./only_test/subformer_basic.rs" ); +include!( "./only_test/subform_basic.rs" ); diff --git a/module/core/former/tests/inc/former_tests/subformer_container_setter_off.rs b/module/core/former/tests/inc/former_tests/subform_collection_setter_off.rs similarity index 70% rename from module/core/former/tests/inc/former_tests/subformer_container_setter_off.rs rename to module/core/former/tests/inc/former_tests/subform_collection_setter_off.rs index 5f3ca56708..fa01385e98 100644 --- a/module/core/former/tests/inc/former_tests/subformer_container_setter_off.rs +++ b/module/core/former/tests/inc/former_tests/subform_collection_setter_off.rs @@ -17,7 +17,7 @@ pub struct Child // #[ derive( Debug, Default, PartialEq ) ] pub struct Parent { - #[ container( setter = false ) ] + #[ subform_collection( setter = false ) ] // #[ scalar( setter = false ) ] children : Vec< Child >, } @@ -31,21 +31,21 @@ where pub fn children( self ) -> &'static str { r#" - Scalar setter `children` should not be generated by default if container is used. + Scalar setter `children` should not be generated by default if collection is used. It can only be generated if req "# } #[ inline( always ) ] - pub fn children2( self ) -> former::ContainerFormer:: + pub fn children2( self ) -> former::CollectionFormer:: < Child, - former::VectorDefinition< Child, Self, Self, ParentFormerAssignChildrenEnd< Definition >, > + former::VectorDefinition< Child, Self, Self, ParentSubformCollectionChildrenEnd< Definition >, > > { - self._children_container_former::< _ >() + self._children_subform_collection::< _ >() } } -include!( "./only_test/subformer_container_children2.rs" ); +include!( "./only_test/subform_collection_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/subform_collection_setter_on.rs similarity index 59% rename from module/core/former/tests/inc/former_tests/subformer_container_setter_on.rs rename to module/core/former/tests/inc/former_tests/subform_collection_setter_on.rs index 83233366cf..0f35a3c2a0 100644 --- a/module/core/former/tests/inc/former_tests/subformer_container_setter_on.rs +++ b/module/core/former/tests/inc/former_tests/subform_collection_setter_on.rs @@ -17,9 +17,9 @@ pub struct Child // #[ derive( Debug, Default, PartialEq ) ] pub struct Parent { - // Such parameters switch off generation of front-end container setter and switch on scalar setter. + // Such parameters switch off generation of front-end collection setter and switch on scalar setter. // Without explicit scalar_setter( true ) scalar setter is not generated. - #[ subform( setter = false ) ] + #[ subform_entry( setter = false ) ] #[ scalar( setter = true ) ] children : Vec< Child >, } @@ -30,16 +30,16 @@ where { #[ inline( always ) ] - pub fn children2( self ) -> former::ContainerFormer:: + pub fn children2( self ) -> former::CollectionFormer:: < Child, - former::VectorDefinition< Child, Self, Self, ParentFormerAssignChildrenEnd< Definition >, > + former::VectorDefinition< Child, Self, Self, ParentSubformCollectionChildrenEnd< Definition >, > > { - self._children_container_former::< _ >() + self._children_subform_collection::< _ >() } } -include!( "./only_test/subformer_scalar_children.rs" ); -include!( "./only_test/subformer_container_children2.rs" ); +include!( "./only_test/subform_scalar_children.rs" ); +include!( "./only_test/subform_collection_children2.rs" ); diff --git a/module/core/former/tests/inc/former_tests/subformer_subform.rs b/module/core/former/tests/inc/former_tests/subform_entry.rs similarity index 82% rename from module/core/former/tests/inc/former_tests/subformer_subform.rs rename to module/core/former/tests/inc/former_tests/subform_entry.rs index e112d38ecd..063fec5dc4 100644 --- a/module/core/former/tests/inc/former_tests/subformer_subform.rs +++ b/module/core/former/tests/inc/former_tests/subform_entry.rs @@ -17,7 +17,7 @@ pub struct Child // #[ derive( Debug, Default, PartialEq ) ] pub struct Parent { - #[ subform( setter = false ) ] + #[ subform_entry( setter = false ) ] children : Vec< Child >, } @@ -29,14 +29,14 @@ where #[ inline( always ) ] pub fn child( self, name : &str ) -> ChildAsSubformer< Self, impl ChildAsSubformerEnd< Self > > { - self._children_add::< ChildFormer< _ >, _, >() + self._children_subform_entry::< ChildFormer< _ >, _, >() .name( name ) } #[ inline( always ) ] pub fn _child( self ) -> ChildAsSubformer< Self, impl ChildAsSubformerEnd< Self > > { - self._children_add + self._children_subform_entry ::< < Child as former::EntityToFormer< _ > >::Former, _, >() } @@ -46,4 +46,4 @@ where // == end of generated -include!( "./only_test/subformer_subform_child.rs" ); +include!( "./only_test/subform_entry_child.rs" ); diff --git a/module/core/former/tests/inc/former_tests/subformer_subform_hashmap.rs b/module/core/former/tests/inc/former_tests/subform_entry_hashmap.rs similarity index 98% rename from module/core/former/tests/inc/former_tests/subformer_subform_hashmap.rs rename to module/core/former/tests/inc/former_tests/subform_entry_hashmap.rs index 2c0ad7e8d7..48bcddf617 100644 --- a/module/core/former/tests/inc/former_tests/subformer_subform_hashmap.rs +++ b/module/core/former/tests/inc/former_tests/subform_entry_hashmap.rs @@ -19,7 +19,7 @@ pub struct Child // #[ derive( Debug, PartialEq ) ] pub struct Parent { - #[ subform ] + #[ subform_entry ] command : HashMap< String, Child >, } diff --git a/module/core/former/tests/inc/former_tests/subformer_subform_hashmap_custom.rs b/module/core/former/tests/inc/former_tests/subform_entry_hashmap_custom.rs similarity index 89% rename from module/core/former/tests/inc/former_tests/subformer_subform_hashmap_custom.rs rename to module/core/former/tests/inc/former_tests/subform_entry_hashmap_custom.rs index 175197fab8..1b1dce6e63 100644 --- a/module/core/former/tests/inc/former_tests/subformer_subform_hashmap_custom.rs +++ b/module/core/former/tests/inc/former_tests/subform_entry_hashmap_custom.rs @@ -19,7 +19,7 @@ pub struct Child // #[ derive( Debug, PartialEq ) ] pub struct Parent { - #[ subform( setter = false ) ] + #[ subform_entry( setter = false ) ] command : HashMap< String, Child >, } @@ -31,7 +31,7 @@ where // more generic version #[ inline( always ) ] - pub fn _children_add_with_closure< Former2, Definition2, Types2 >( self ) -> + pub fn _children_subform_entry_with_closure< Former2, Definition2, Types2 >( self ) -> Former2 where Types2 : former::FormerDefinitionTypes @@ -63,10 +63,10 @@ where } if let Some( ref mut children ) = super_former.storage.command { - former::ContainerAdd::add + former::CollectionAdd::add ( children, - < < HashMap< String, Child > as former::Container >::Val as former::ValToEntry< HashMap< String, Child > > > + < < HashMap< String, Child > as former::Collection >::Val as former::ValToEntry< HashMap< String, Child > > > ::val_to_entry( former::StoragePreform::preform( substorage ) ) ); } @@ -75,15 +75,15 @@ where Former2::former_begin( None, Some( self ), former::FormingEndClosure::new( on_end ) ) } - // reuse _command_add + // reuse _command_subform_entry #[ inline( always ) ] pub fn command( self, name : &str ) -> ChildAsSubformer< Self, impl ChildAsSubformerEnd< Self > > { - self._command_add::< ChildFormer< _ >, _, >() + self._command_subform_entry::< ChildFormer< _ >, _, >() .name( name ) } - // that's how you should do custom subformer setters if you can't reuse _command_add + // that's how you should do custom subformer setters if you can't reuse _command_subform_entry #[ inline( always ) ] pub fn command2( self, name : &str ) -> ChildAsSubformer< Self, impl ChildAsSubformerEnd< Self > > { @@ -97,12 +97,12 @@ where super_former.storage.command = Some( Default::default() ); } - // add instance to the container + // add instance to the collection super_former.storage.command.as_mut().unwrap() .entry( preformed.name.clone() ) .or_insert( preformed.clone() ); - // custom logic to add two instances to the container + // custom logic to add two instances to the collection super_former.storage.command.as_mut().unwrap() .entry( format!( "{}_2", preformed.name ) ) .or_insert( preformed.clone() ); diff --git a/module/core/former/tests/inc/former_tests/subformer_subform_manual.rs b/module/core/former/tests/inc/former_tests/subform_entry_manual.rs similarity index 73% rename from module/core/former/tests/inc/former_tests/subformer_subform_manual.rs rename to module/core/former/tests/inc/former_tests/subform_entry_manual.rs index dd75b254c0..2a210b97bb 100644 --- a/module/core/former/tests/inc/former_tests/subformer_subform_manual.rs +++ b/module/core/former/tests/inc/former_tests/subform_entry_manual.rs @@ -15,8 +15,8 @@ pub struct Child // #[ derive( Debug, Default, PartialEq ) ] pub struct Parent { - // #[ container( definition = former::VectorDefinition ) ] - // #[ subform ] + // #[ subform_collection( definition = former::VectorDefinition ) ] + // #[ subform_entry ] #[ scalar( setter = false ) ] children : Vec< Child >, } @@ -30,7 +30,7 @@ where { #[ inline( always ) ] - pub fn _children_add_with_closure< Former2, Definition2, Types2 >( self ) -> + pub fn _children_subform_entry_with_closure< Former2, Definition2, Types2 >( self ) -> Former2 where Types2 : former::FormerDefinitionTypes @@ -62,10 +62,10 @@ where } if let Some( ref mut children ) = super_former.storage.children { - former::ContainerAdd::add + former::CollectionAdd::add ( children, - < < Vec< Child > as former::Container >::Val as former::ValToEntry< Vec< Child > > > + < < Vec< Child > as former::Collection >::Val as former::ValToEntry< Vec< Child > > > ::val_to_entry( former::StoragePreform::preform( substorage ) ) ); } @@ -74,14 +74,12 @@ where 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 + self._children_subform_entry ::< ChildFormer< _ >, _, >() .name( name ) } @@ -90,24 +88,24 @@ where // pub fn _child( self ) -> // ChildAsSubformer< Self, impl ChildAsSubformerEnd< Self > > // { - // self._children_add + // self._children_subform_entry // ::< < Child as former::EntityToFormer< _ > >::Former, _, >() // } // it is generated #[ inline( always ) ] pub fn _child( self ) -> - < < Vec< Child > as former::Container >::Entry as former::EntityToFormer + < < Vec< Child > as former::Collection >::Entry as former::EntityToFormer < - // ChildFormerDefinition< Self, Self, ParentFormerAddChildrenEnd< Definition > >, + // ChildFormerDefinition< Self, Self, ParentSubformEntryChildrenEnd< Definition > >, < - < Vec< Child > as former::Container >::Entry as former::EntityToDefinition< Self, Self, ParentFormerAddChildrenEnd< Definition > > + < Vec< Child > as former::Collection >::Entry as former::EntityToDefinition< Self, Self, ParentSubformEntryChildrenEnd< Definition > > >::Definition, > >::Former { - self._children_add - ::< < < Vec< Child > as former::Container >::Entry as former::EntityToFormer< _ > >::Former, _, >() + self._children_subform_entry + ::< < < Vec< Child > as former::Collection >::Entry as former::EntityToFormer< _ > >::Former, _, >() } } @@ -121,12 +119,12 @@ where { #[ inline( always ) ] - pub fn _children_add< Former2, Definition2 >( self ) -> + pub fn _children_subform_entry< Former2, Definition2 >( self ) -> Former2 where Definition2 : former::FormerDefinition < - End = ParentFormerAddChildrenEnd< Definition >, + End = ParentSubformEntryChildrenEnd< Definition >, Storage = < Child as former::EntityToStorage >::Storage, Formed = Self, Context = Self, @@ -139,19 +137,19 @@ where >, Former2 : former::FormerBegin< Definition2 >, { - Former2::former_begin( None, Some( self ), ParentFormerAddChildrenEnd::default() ) + Former2::former_begin( None, Some( self ), ParentSubformEntryChildrenEnd::default() ) } } -/// Handles the completion of and element of subformer's container. -pub struct ParentFormerAddChildrenEnd< Definition > +/// Handles the completion of and element of subformer's collection. +pub struct ParentSubformEntryChildrenEnd< Definition > { _phantom : core::marker::PhantomData< fn( Definition ) >, } impl< Definition > Default -for ParentFormerAddChildrenEnd< Definition > +for ParentSubformEntryChildrenEnd< Definition > { #[ inline( always ) ] fn default() -> Self @@ -164,7 +162,7 @@ for ParentFormerAddChildrenEnd< Definition > } impl< Types2, Definition > former::FormingEnd< Types2, > -for ParentFormerAddChildrenEnd< Definition > +for ParentSubformEntryChildrenEnd< Definition > where Definition : former::FormerDefinition < @@ -172,7 +170,7 @@ where >, Types2 : former::FormerDefinitionTypes < - Storage = < < Vec< Child > as former::Container >::Entry as former::EntityToStorage >::Storage, + Storage = < < Vec< Child > as former::Collection >::Entry as former::EntityToStorage >::Storage, Formed = ParentFormer< Definition >, Context = ParentFormer< Definition >, >, @@ -193,7 +191,7 @@ where } if let Some( ref mut fields ) = super_former.storage.children { - former::ContainerAdd::add( fields, former::StoragePreform::preform( substorage ) ); + former::CollectionAdd::add( fields, former::StoragePreform::preform( substorage ) ); } super_former } @@ -201,4 +199,4 @@ where // == end of generated for Parent in context of attribute subform -include!( "./only_test/subformer_subform_child.rs" ); +include!( "./only_test/subform_entry_child.rs" ); diff --git a/module/core/former/tests/inc/former_tests/subformer_subform_named.rs b/module/core/former/tests/inc/former_tests/subform_entry_named.rs similarity index 89% rename from module/core/former/tests/inc/former_tests/subformer_subform_named.rs rename to module/core/former/tests/inc/former_tests/subform_entry_named.rs index e834e8b30d..37e2c79d55 100644 --- a/module/core/former/tests/inc/former_tests/subformer_subform_named.rs +++ b/module/core/former/tests/inc/former_tests/subform_entry_named.rs @@ -17,7 +17,7 @@ pub struct Child // #[ derive( Debug, Default, PartialEq ) ] pub struct Parent { - #[ subform( name = _child ) ] + #[ subform_entry( name = _child ) ] children : Vec< Child >, } @@ -40,7 +40,7 @@ where pub fn child( self, name : &str ) -> ChildAsSubformer< Self, impl ChildAsSubformerEnd< Self > > { - self._children_add + self._children_subform_entry ::< ChildFormer< _ >, _, >() .name( name ) } @@ -49,7 +49,7 @@ where // pub fn _child( self ) -> // ChildAsSubformer< Self, impl ChildAsSubformerEnd< Self > > // { - // self._children_add + // self._children_subform_entry // ::< < Child as former::EntityToFormer< _ > >::Former, _, >() // } @@ -59,4 +59,4 @@ where // == end of generated -include!( "./only_test/subformer_subform_child.rs" ); +include!( "./only_test/subform_entry_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/subform_entry_named_manual.rs similarity index 69% rename from module/core/former/tests/inc/former_tests/subformer_subform_named_manual.rs rename to module/core/former/tests/inc/former_tests/subform_entry_named_manual.rs index c169a74d79..3d0542c592 100644 --- a/module/core/former/tests/inc/former_tests/subformer_subform_named_manual.rs +++ b/module/core/former/tests/inc/former_tests/subform_entry_named_manual.rs @@ -17,7 +17,7 @@ pub struct Child // #[ derive( Debug, Default, PartialEq ) ] pub struct Parent { - #[ subform ] + #[ subform_entry ] // #[ scalar( setter = false ) ] children : Vec< Child >, } @@ -33,7 +33,7 @@ where pub fn child( self, name : &str ) -> ChildAsSubformer< Self, impl ChildAsSubformerEnd< Self > > { - self._children_add + self._children_subform_entry ::< ChildFormer< _ >, _, >() .name( name ) } @@ -42,23 +42,23 @@ where // pub fn _child( self ) -> // ChildAsSubformer< Self, impl ChildAsSubformerEnd< Self > > // { - // self._children_add + // self._children_subform_entry // ::< < Child as former::EntityToFormer< _ > >::Former, _, >() // } #[ inline( always ) ] pub fn _child( self ) -> - < < Vec< Child > as former::Container >::Entry as former::EntityToFormer + < < Vec< Child > as former::Collection >::Entry as former::EntityToFormer < - // ChildFormerDefinition< Self, Self, ParentFormerAddChildrenEnd< Definition > >, + // ChildFormerDefinition< Self, Self, ParentSubformEntryChildrenEnd< Definition > >, < - < Vec< Child > as former::Container >::Entry as former::EntityToDefinition< Self, Self, ParentFormerAddChildrenEnd< Definition > > + < Vec< Child > as former::Collection >::Entry as former::EntityToDefinition< Self, Self, ParentSubformEntryChildrenEnd< Definition > > >::Definition, > >::Former { - self._children_add - ::< < < Vec< Child > as former::Container >::Entry as former::EntityToFormer< _ > >::Former, _, >() + self._children_subform_entry + ::< < < Vec< Child > as former::Collection >::Entry as former::EntityToFormer< _ > >::Former, _, >() } } @@ -69,4 +69,4 @@ where // == end of generated for Parent in context of attribute subform -include!( "./only_test/subformer_subform_child.rs" ); +include!( "./only_test/subform_entry_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/subform_entry_setter_off.rs similarity index 87% rename from module/core/former/tests/inc/former_tests/subformer_subform_setter_off.rs rename to module/core/former/tests/inc/former_tests/subform_entry_setter_off.rs index e5a2f9eb61..ae08d3c05c 100644 --- a/module/core/former/tests/inc/former_tests/subformer_subform_setter_off.rs +++ b/module/core/former/tests/inc/former_tests/subform_entry_setter_off.rs @@ -17,7 +17,7 @@ pub struct Child // #[ derive( Debug, Default, PartialEq ) ] pub struct Parent { - #[ subform( setter = false ) ] + #[ subform_entry( setter = false ) ] children : Vec< Child >, } @@ -39,11 +39,11 @@ where pub fn children2( self, name : &str ) -> ChildAsSubformer< Self, impl ChildAsSubformerEnd< Self > > { - self._children_add + self._children_subform_entry ::< ChildFormer< _ >, _, >() .name( name ) } } -include!( "./only_test/subformer_subform_children2.rs" ); +include!( "./only_test/subform_entry_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/subform_entry_setter_on.rs similarity index 83% rename from module/core/former/tests/inc/former_tests/subformer_subform_setter_on.rs rename to module/core/former/tests/inc/former_tests/subform_entry_setter_on.rs index 29378ff208..fd5608463e 100644 --- a/module/core/former/tests/inc/former_tests/subformer_subform_setter_on.rs +++ b/module/core/former/tests/inc/former_tests/subform_entry_setter_on.rs @@ -19,7 +19,7 @@ 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 ) ] + #[ subform_entry( setter = false ) ] #[ scalar( setter = true ) ] children : Vec< Child >, } @@ -33,12 +33,12 @@ where pub fn children2( self, name : &str ) -> ChildAsSubformer< Self, impl ChildAsSubformerEnd< Self > > { - self._children_add + self._children_subform_entry ::< ChildFormer< _ >, _, >() .name( name ) } } -include!( "./only_test/subformer_scalar_children.rs" ); -include!( "./only_test/subformer_subform_children2.rs" ); +include!( "./only_test/scalar_children.rs" ); +include!( "./only_test/subform_entry_children2.rs" ); diff --git a/module/core/former/tests/inc/former_tests/subform_scalar.rs b/module/core/former/tests/inc/former_tests/subform_scalar.rs new file mode 100644 index 0000000000..bf081269fb --- /dev/null +++ b/module/core/former/tests/inc/former_tests/subform_scalar.rs @@ -0,0 +1,28 @@ +#![ 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_scalar ] + child : Child, +} + +// == begin of generated + +// == end of generated + +include!( "./only_test/subform_scalar.rs" ); diff --git a/module/core/former/tests/inc/former_tests/subform_scalar_manual.rs b/module/core/former/tests/inc/former_tests/subform_scalar_manual.rs new file mode 100644 index 0000000000..d0d8ef9608 --- /dev/null +++ b/module/core/former/tests/inc/former_tests/subform_scalar_manual.rs @@ -0,0 +1,140 @@ +#![ 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 +{ + #[ scalar( setter = false ) ] + // #[ scalar_subform ] + child : Child, +} + +impl< Definition > ParentFormer< Definition > +where + Definition : former::FormerDefinition< Storage = < Parent as former::EntityToStorage >::Storage >, +{ + + #[ inline( always ) ] + pub fn _child_subform_scalar< Former2, Definition2 >( self ) -> + Former2 + where + Definition2 : former::FormerDefinition + < + End = ParentFormerSubformScalarChildEnd< 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 ), ParentFormerSubformScalarChildEnd::default() ) + } + +} + +impl< Definition > ParentFormer< Definition > +where + Definition : former::FormerDefinition< Storage = < Parent as former::EntityToStorage >::Storage >, +{ + + #[ inline( always ) ] + pub fn child( self ) -> + ChildAsSubformer< Self, impl ChildAsSubformerEnd< Self > > + { + self._child_subform_scalar + ::< < Child as former::EntityToFormer< _ > >::Former, _, >() + } + +} + +// = end + +/// Represents the endpoint for the forming process of a scalar field managed by a subformer within a `Parent` entity. +/// +/// This structure is a critical component of the forming process when using a subform scalar setter. It handles +/// the finalization of the scalar field's value that has been configured through its dedicated subformer. +/// Essentially, this end action integrates the individually formed scalar value back into the parent structure. +/// +/// ## Type Parameters +/// +/// - `Definition`: The type that defines the former setup for the `Parent` entity, influencing storage and behavior during forming. +/// +/// ## Parameters of `call` +/// +/// - `substorage`: Storage type specific to the `Child`, containing the newly formed scalar value. +/// - `super_former`: An optional context of the `ParentFormer`, which will receive the value. The function ensures +/// that this context is not `None` and inserts the formed value into the designated field within `Parent`'s storage. +/// + +pub struct ParentFormerSubformScalarChildEnd< Definition > +{ + _phantom : core::marker::PhantomData< fn( Definition ) >, +} + +impl< Definition > Default +for ParentFormerSubformScalarChildEnd< Definition > +{ + #[ inline( always ) ] + fn default() -> Self + { + Self + { + _phantom : core::marker::PhantomData, + } + } +} + +impl< Types2, Definition > former::FormingEnd< Types2, > +for ParentFormerSubformScalarChildEnd< Definition > +where + Definition : former::FormerDefinition + < + Storage = < Parent as former::EntityToStorage >::Storage, + >, + Types2 : former::FormerDefinitionTypes + < + Storage = < Child 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(); + debug_assert!( super_former.storage.child.is_none() ); + super_former.storage.child = Some( ::core::convert::Into::into( former::StoragePreform::preform( substorage ) ) ); + super_former + } +} + +// == begin of generated + +// == end of generated + +include!( "./only_test/subform_scalar.rs" ); diff --git a/module/core/former/tests/inc/former_tests/subform_scalar_name.rs b/module/core/former/tests/inc/former_tests/subform_scalar_name.rs new file mode 100644 index 0000000000..87a0d52ded --- /dev/null +++ b/module/core/former/tests/inc/former_tests/subform_scalar_name.rs @@ -0,0 +1,73 @@ +#![ 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_scalar( name = child2 ) ] + child : Child, +} + +impl< Definition > ParentFormer< Definition > +where + Definition : former::FormerDefinition< Storage = < Parent as former::EntityToStorage >::Storage >, +{ + + pub fn child() + { + } + + #[ inline( always ) ] + pub fn child3( self ) -> + ChildAsSubformer< Self, impl ChildAsSubformerEnd< Self > > + { + self._child_subform_scalar + ::< < Child as former::EntityToFormer< _ > >::Former, _, >() + } + +} + +// == begin of generated + +// == end of generated + +#[ test ] +fn subforme_scalar_2() +{ + + let got = Parent::former() + .child2().name( "a" ).data( true ).end() + .form(); + + let exp = Parent { child : Child { name : "a".to_string(), data : true } }; + a_id!( got, exp ); + +} + +#[ test ] +fn subforme_scalar_3() +{ + + let got = Parent::former() + .child3().name( "a" ).data( true ).end() + .form(); + + let exp = Parent { child : Child { name : "a".to_string(), data : true } }; + a_id!( got, exp ); + +} + +// qqq : write tests similar to `subform_all` which apply attributes `scalar`, `subform_entry` and `subform_scalar` on the same field and check all three attribtues don't interfere with each other diff --git a/module/core/former/tests/inc/former_tests/tuple_struct.rs b/module/core/former/tests/inc/former_tests/tuple_struct.rs new file mode 100644 index 0000000000..2925f0f592 --- /dev/null +++ b/module/core/former/tests/inc/former_tests/tuple_struct.rs @@ -0,0 +1,37 @@ +#![ deny( missing_docs ) ] + +#[ allow( unused_imports ) ] +use super::*; + +// xxx : qqq : make that working + +// use collection_tools::HashMap; +// +// type Key = &'static str; +// type Value = &'static str; +// +// #[ derive( Debug, PartialEq, former::Former ) ] +// pub struct Struct1( #[ subform_collection ] HashMap< Key, Value > ); +// +// impl Struct1 +// { +// pub fn get( &self, key : Key ) -> Option< &Value > +// { +// self.0.get( key ) +// } +// } +// +// #[ test ] +// fn example() +// { +// // form a key-value store +// let instance = Struct1::former() +// .map() +// .add( ( "first", "Value1" ) ) +// .add( ( "second", "Value2" ) ) +// .end() +// .form(); +// +// // now it is a read-only storage with pre-configured data +// assert_eq!( Some( &"Value1" ), instance.get( "first" ) ); +// } diff --git a/module/core/former/tests/inc/mod.rs b/module/core/former/tests/inc/mod.rs index 069cbec9e2..30bcd81c0f 100644 --- a/module/core/former/tests/inc/mod.rs +++ b/module/core/former/tests/inc/mod.rs @@ -9,27 +9,28 @@ 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; + // = basic + #[ cfg( any( feature = "use_alloc", not( feature = "no_std" ) ) ) ] mod a_basic_manual; + #[ cfg( any( feature = "use_alloc", not( feature = "no_std" ) ) ) ] mod a_basic; mod a_primitives_manual; mod a_primitives; + mod tuple_struct; - mod a_containers_scalar; - #[ cfg( not( feature = "no_std" ) ) ] - mod a_containers_manual; - #[ cfg( not( feature = "no_std" ) ) ] - mod a_containers; + #[ cfg( any( feature = "use_alloc", not( feature = "no_std" ) ) ) ] + mod subform_collection_basic_scalar; + #[ cfg( any( feature = "use_alloc", not( feature = "no_std" ) ) ) ] + mod subform_collection_basic_manual; + #[ cfg( any( feature = "use_alloc", not( feature = "no_std" ) ) ) ] + mod subform_collection_basic; - mod attribute_default_container; + // = attribute + + #[ cfg( any( feature = "use_alloc", not( feature = "no_std" ) ) ) ] + mod attribute_default_collection; + #[ cfg( any( feature = "use_alloc", not( feature = "no_std" ) ) ) ] mod attribute_default_primitive; mod attribute_default_conflict; mod attribute_storage_with_end; @@ -38,21 +39,20 @@ mod former_tests mod attribute_setter; mod attribute_alias; mod attribute_feature; + mod attribute_multiple; - mod string_slice_manual; - mod string_slice; - mod unsigned_primitive_types; - mod default_user_type; - mod user_type_no_default; - mod user_type_no_debug; - mod visibility; + // = name collision mod name_collision_former_hashmap_without_parameter; mod name_collision_former_vector_without_parameter; + #[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] mod name_collisions; mod name_collision_context; mod name_collision_end; mod name_collision_on_end; + mod name_collision_core; + + // = parametrization #[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] mod parametrized_struct_manual; @@ -63,46 +63,92 @@ mod former_tests mod parametrized_field; mod parametrized_field_where; + mod parametrized_slice_manual; + mod parametrized_slice; + + // = etc + + mod unsigned_primitive_types; + mod default_user_type; + mod user_type_no_default; + mod user_type_no_debug; + mod visibility; + + // = collection former + + #[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] + mod collection_former_common; + #[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] + mod collection_former_btree_map; + #[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] + mod collection_former_btree_set; + #[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] + mod collection_former_binary_heap; + #[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] + mod collection_former_hashmap; + #[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] + mod collection_former_hashset; + #[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] + mod collection_former_linked_list; + #[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] + mod collection_former_vec; + #[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] + mod collection_former_vec_deque; + + // = subform collection + + #[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] + mod subform_collection_playground; + #[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] + mod subform_collection; + #[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] + mod subform_collection_manual; + #[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] + mod subform_collection_implicit; + #[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] + mod subform_collection_setter_off; + #[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] + mod subform_collection_named; + #[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] + mod subform_collection_custom; + + // = subform scalar + + #[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] + mod subform_scalar_manual; + #[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] + mod subform_scalar; + #[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] + mod subform_scalar_name; + + // = subform entry + + #[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] + mod subform_entry; + #[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] + mod subform_entry_manual; + #[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] + mod subform_entry_named; + #[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] + mod subform_entry_named_manual; + #[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] + mod subform_entry_setter_off; + #[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] + mod subform_entry_setter_on; + + #[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] + mod subform_entry_hashmap; + #[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] + mod subform_entry_hashmap_custom; + + // = subform all : scalar, subform_scalar, subform_entry, subform_collection + + #[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] + mod subform_all; + #[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] + mod subform_all_private; #[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] - mod subformer_basic; - - #[ cfg( any( not( feature = "no_std" ) ) ) ] - mod subformer_container; - #[ cfg( any( not( feature = "no_std" ) ) ) ] - mod subformer_container_manual; - #[ cfg( any( not( feature = "no_std" ) ) ) ] - mod subformer_container_implicit; - #[ cfg( any( not( feature = "no_std" ) ) ) ] - mod subformer_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_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_private; - #[ cfg( any( not( feature = "no_std" ) ) ) ] - mod subformer_subform_and_container_parametrized; + mod subform_all_parametrized; } diff --git a/module/core/former/tests/tests.rs b/module/core/former/tests/tests.rs index a82c4bfb53..fe0db783b8 100644 --- a/module/core/former/tests/tests.rs +++ b/module/core/former/tests/tests.rs @@ -6,4 +6,5 @@ use test_tools::exposed::*; #[ allow( unused_imports ) ] use former as the_module; +#[ cfg( feature = "enabled" ) ] mod inc; diff --git a/module/core/former_meta/Cargo.toml b/module/core/former_meta/Cargo.toml index 4bb60c3283..04ea67bfca 100644 --- a/module/core/former_meta/Cargo.toml +++ b/module/core/former_meta/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "former_meta" -version = "1.0.0" +version = "2.1.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", @@ -44,7 +44,7 @@ full = [ "derive_components_assign", "derive_from_components", ] -enabled = [ "macro_tools/enabled", "iter_tools/enabled" ] +enabled = [ "macro_tools/enabled", "iter_tools/enabled", "former_types/enabled" ] derive_former = [ "convert_case" ] derive_components = [] @@ -57,9 +57,12 @@ derive_from_components = [ "derive_components" ] proc-macro = true [dependencies] -macro_tools = { workspace = true } +macro_tools = { workspace = true } # qqq : optimize set of features +former_types = { workspace = true, features = [ "enabled", "types_component_assign" ] } iter_tools = { workspace = true } convert_case = { version = "0.6.0", default-features = false, optional = true, features = [] } +const_format = { version = "0.2.32" } +# zzz : reexport const_format [dev-dependencies] test_tools = { workspace = true, features = [ "full" ] } diff --git a/module/core/former_meta/src/component/component_assign.rs b/module/core/former_meta/src/component/component_assign.rs index 4677f27b0f..de12fc7f5f 100644 --- a/module/core/former_meta/src/component/component_assign.rs +++ b/module/core/former_meta/src/component/component_assign.rs @@ -1,18 +1,19 @@ use super::*; -use macro_tools::{ attr, diag, type_struct, Result }; +use macro_tools::{ attr, diag, Result }; /// -/// Generates implementations of the `ComponentAssign` trait for each field of a struct. +/// Generates implementations of the `Assign` trait for each field of a struct. /// pub fn component_assign( input : proc_macro::TokenStream ) -> Result< proc_macro2::TokenStream > { let original_input = input.clone(); - let parsed = syn::parse::< type_struct::TypeStructParsed >( input )?; - let has_debug = attr::has_debug( parsed.item.attrs.iter() )?; + let parsed = syn::parse::< syn::ItemStruct >( input )?; + let has_debug = attr::has_debug( parsed.attrs.iter() )?; + let item_name = &parsed.ident.clone(); - let for_field = parsed.fields_many().iter().map( | field | + let for_field = parsed.fields.iter().map( | field | { - for_each_field( field, &parsed.item_name ) + for_each_field( field, &parsed.ident ) }) .collect::< Result< Vec< _ > > >()?; @@ -23,23 +24,18 @@ pub fn component_assign( input : proc_macro::TokenStream ) -> Result< proc_macro if has_debug { - let about = format!( "derive : ComponentAssign\nstructure : {0}", &parsed.item_name ); + let about = format!( "derive : Assign\nstructure : {item_name}" ); diag::report_print( about, &original_input, &result ); } - // if has_debug - // { - // diag::report_print( "derive : ComponentAssign", original_input, &result ); - // } - Ok( result ) } -/// Generates an implementation of the `ComponentAssign` trait for a specific field of a struct. +/// Generates an implementation of the `Assign` trait for a specific field of a struct. /// /// This function creates the trait implementation that enables setting a struct's field value /// with a type that can be converted into the field's type. It dynamically generates code -/// during the macro execution to provide `ComponentAssign` trait implementations for each field +/// during the macro execution to provide `Assign` trait implementations for each field /// of the struct, facilitating an ergonomic API for modifying struct instances. /// /// # Parameters @@ -50,7 +46,7 @@ pub fn component_assign( input : proc_macro::TokenStream ) -> Result< proc_macro /// # Example of generated code /// /// ```rust, ignore -/// impl< IntoT > former::ComponentAssign< i32, IntoT > for Options1 +/// impl< IntoT > former::Assign< i32, IntoT > for Options1 /// where /// IntoT : Into< i32 >, /// { @@ -70,7 +66,7 @@ fn for_each_field( field : &syn::Field, item_name : &syn::Ident ) -> Result< pro Ok( qt! { #[ allow( non_snake_case ) ] - impl< IntoT > ComponentAssign< #field_type, IntoT > for #item_name + impl< IntoT > Assign< #field_type, IntoT > for #item_name where IntoT : Into< #field_type >, { diff --git a/module/core/former_meta/src/component/component_from.rs b/module/core/former_meta/src/component/component_from.rs index 994206b996..c5613a48fa 100644 --- a/module/core/former_meta/src/component/component_from.rs +++ b/module/core/former_meta/src/component/component_from.rs @@ -1,17 +1,18 @@ use super::*; -use macro_tools::{ attr, diag, type_struct, Result }; +use macro_tools::{ attr, diag, Result }; /// Generates `From` implementations for each unique component (field) of the structure. pub fn component_from( input : proc_macro::TokenStream ) -> Result< proc_macro2::TokenStream > { let original_input = input.clone(); - let parsed = syn::parse::< type_struct::TypeStructParsed >( input )?; - let has_debug = attr::has_debug( parsed.item.attrs.iter() )?; + let parsed = syn::parse::< syn::ItemStruct >( input )?; + let has_debug = attr::has_debug( parsed.attrs.iter() )?; + let item_name = &parsed.ident; - let for_field = parsed.fields_many().iter().map( | field | + let for_field = parsed.fields.iter().map( | field | { - for_each_field( field, &parsed.item_name ) + for_each_field( field, &parsed.ident ) }) .collect::< Result< Vec< _ > > >()?; @@ -22,7 +23,7 @@ pub fn component_from( input : proc_macro::TokenStream ) -> Result< proc_macro2: if has_debug { - let about = format!( "derive : ComponentFrom\nstructure : {0}", &parsed.item_name ); + let about = format!( "derive : ComponentFrom\nstructure : {item_name}" ); diag::report_print( about, &original_input, &result ); } diff --git a/module/core/former_meta/src/component/components_assign.rs b/module/core/former_meta/src/component/components_assign.rs index bcf5cc0bc9..6b495e7629 100644 --- a/module/core/former_meta/src/component/components_assign.rs +++ b/module/core/former_meta/src/component/components_assign.rs @@ -1,6 +1,6 @@ use super::*; -use macro_tools::{ attr, diag, type_struct, Result }; -use iter_tools::{ Itertools, process_results }; +use macro_tools::{ attr, diag, Result, format_ident }; +use iter_tools::{ Itertools }; /// /// Generate `ComponentsAssign` trait implementation for the type, providing `components_assign` function @@ -12,17 +12,24 @@ pub fn components_assign( input : proc_macro::TokenStream ) -> Result< proc_macr { use convert_case::{ Case, Casing }; let original_input = input.clone(); - let parsed = syn::parse::< type_struct::TypeStructParsed >( input )?; - let has_debug = attr::has_debug( parsed.item.attrs.iter() )?; + let parsed = syn::parse::< syn::ItemStruct >( input )?; + let has_debug = attr::has_debug( parsed.attrs.iter() )?; // 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 ) ); - let method_ident = syn::Ident::new( &method_name, item_name.span() ); + let item_name = &parsed.ident; + let trait_ident = format_ident! + { + "{}ComponentsAssign", + item_name + }; + let method_ident = format_ident! + { + "{}_assign", + item_name.to_string().to_case( Case::Snake ) + }; // fields +// fields let ( bounds1, bounds2, component_assigns ) : ( Vec< _ >, Vec< _ >, Vec< _ > ) = parsed.fields.iter().map( | field | { let field_type = &field.ty; @@ -32,9 +39,9 @@ pub fn components_assign( input : proc_macro::TokenStream ) -> Result< proc_macr ( bound1, bound2, component_assign ) }).multiunzip(); - let bounds1 : Vec< _ > = process_results( bounds1, | iter | iter.collect() )?; - let bounds2 : Vec< _ > = process_results( bounds2, | iter | iter.collect() )?; - let component_assigns : Vec< _ > = process_results( component_assigns, | iter | iter.collect() )?; + let bounds1 : Vec< _ > = bounds1.into_iter().collect::< Result< _ > >()?; + let bounds2 : Vec< _ > = bounds2.into_iter().collect::< Result< _ > >()?; + let component_assigns : Vec< _ > = component_assigns.into_iter().collect::< Result< _ > >()?; // code let doc = format!( "Interface to assign instance from set of components exposed by a single argument." ); @@ -106,7 +113,7 @@ fn generate_trait_bounds( field_type : &syn::Type ) -> Result< proc_macro2::Toke /// ### Output example /// /// ```ignore -/// T : former::ComponentAssign< i32, IntoT >, +/// T : former::Assign< i32, IntoT >, /// ``` /// fn generate_impl_bounds( field_type : &syn::Type ) -> Result< proc_macro2::TokenStream > @@ -115,7 +122,7 @@ fn generate_impl_bounds( field_type : &syn::Type ) -> Result< proc_macro2::Token ( qt! { - T : former::ComponentAssign< #field_type, IntoT >, + T : former::Assign< #field_type, IntoT >, } ) } @@ -127,7 +134,7 @@ fn generate_impl_bounds( field_type : &syn::Type ) -> Result< proc_macro2::Token /// Output example /// /// ```ignore -/// former::ComponentAssign::< i32, _ >::assign( self.component.clone() ); +/// former::Assign::< i32, _ >::assign( self.component.clone() ); /// ``` /// fn generate_component_assign_call( field : &syn::Field ) -> Result< proc_macro2::TokenStream > @@ -138,7 +145,7 @@ fn generate_component_assign_call( field : &syn::Field ) -> Result< proc_macro2: ( qt! { - former::ComponentAssign::< #field_type, _ >::assign( self, component.clone() ); + former::Assign::< #field_type, _ >::assign( self, component.clone() ); } ) } diff --git a/module/core/former_meta/src/component/from_components.rs b/module/core/former_meta/src/component/from_components.rs index 62ae0615a9..d76029ca0a 100644 --- a/module/core/former_meta/src/component/from_components.rs +++ b/module/core/former_meta/src/component/from_components.rs @@ -1,5 +1,5 @@ use super::*; -use macro_tools::{ attr, diag, type_struct, Result }; +use macro_tools::{ attr, diag, item_struct, Result }; /// /// Generates an implementation of the `From< T >` trait for a custom struct, enabling @@ -36,15 +36,15 @@ use macro_tools::{ attr, diag, type_struct, Result }; pub fn from_components( input : proc_macro::TokenStream ) -> Result< proc_macro2::TokenStream > { let original_input = input.clone(); - let parsed = syn::parse::< type_struct::TypeStructParsed >( input )?; - let has_debug = attr::has_debug( parsed.item.attrs.iter() )?; + let parsed = syn::parse::< syn::ItemStruct >( input )?; + let has_debug = attr::has_debug( parsed.attrs.iter() )?; // Struct name - let item_name = parsed.item_name.clone(); + let item_name = &parsed.ident; // Generate snipets - let trait_bounds = trait_bounds( &parsed.field_types()[ .. ] ); - let field_assigns = field_assign( &parsed.fields_many() ); + let trait_bounds = trait_bounds( item_struct::field_types( &parsed ) ); + let field_assigns = field_assign( parsed.fields.iter() ); let field_names : Vec< _ > = parsed.fields.iter().map( | field | &field.ident ).collect(); // Generate the From trait implementation @@ -69,7 +69,7 @@ pub fn from_components( input : proc_macro::TokenStream ) -> Result< proc_macro2 if has_debug { - let about = format!( "derive : FromComponents\nstructure : {0}", &parsed.item_name ); + let about = format!( "derive : FromComponents\nstructure : {0}", &parsed.ident ); diag::report_print( about, &original_input, &result ); } @@ -98,9 +98,10 @@ pub fn from_components( input : proc_macro::TokenStream ) -> Result< proc_macro2 /// These trait bounds are then used in the `From` implementation to ensure type compatibility. #[ inline ] -fn trait_bounds( field_types : &[ &syn::Type ] ) -> Vec< proc_macro2::TokenStream > +// fn trait_bounds( field_types : &[ &syn::Type ] ) -> Vec< proc_macro2::TokenStream > +fn trait_bounds< 'a >( field_types : impl macro_tools::IterTrait< 'a, &'a syn::Type > ) -> Vec< proc_macro2::TokenStream > { - field_types.iter().map( | field_type | + field_types.map( | field_type | { qt! { @@ -125,9 +126,9 @@ fn trait_bounds( field_types : &[ &syn::Type ] ) -> Vec< proc_macro2::TokenStrea /// #[ inline ] -fn field_assign( fields : &[ &syn::Field ] ) -> Vec< proc_macro2::TokenStream > +fn field_assign< 'a >( fields : impl Iterator< Item = &'a syn::Field > ) -> Vec< proc_macro2::TokenStream > { - fields.iter().map( | field | + fields.map( | field | { let field_ident = &field.ident; let field_type = &field.ty; diff --git a/module/core/former_meta/src/derive_former.rs b/module/core/former_meta/src/derive_former.rs index b934d9cbf9..d79534fb02 100644 --- a/module/core/former_meta/src/derive_former.rs +++ b/module/core/former_meta/src/derive_former.rs @@ -1,15 +1,15 @@ use super::*; -use iter_tools::{ Itertools, process_results }; +use iter_tools::{ Itertools }; use macro_tools::{ attr, diag, generic_params, generic_args, typ, derive, Result }; use proc_macro2::TokenStream; -// qqq : implement interfaces for other containers +// qqq : implement interfaces for other collections -mod field; -use field::*; mod field_attrs; use field_attrs::*; +mod field; +use field::*; mod struct_attrs; use struct_attrs::*; @@ -43,6 +43,8 @@ use struct_attrs::*; pub fn mutator ( + item : &syn::Ident, + original_input : &proc_macro::TokenStream, mutator : &AttributeMutator, former_definition_types : &syn::Ident, former_definition_types_generics_impl : &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, @@ -51,7 +53,7 @@ pub fn mutator ) -> Result< TokenStream > { - let former_mutator_code = if mutator.custom + let former_mutator_code = if mutator.custom.value( false ) { qt!{} } @@ -68,15 +70,15 @@ pub fn mutator } }; - if mutator.hint + if mutator.debug.value( false ) { - let hint = format! + let debug = format! ( r#" = Example of custom mutator impl< {} > former::FormerMutator -for {} < {} > +for {former_definition_types} < {} > where {} {{ @@ -88,11 +90,16 @@ where }} "#, 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}" ); + // println!( "{debug}" ); + let about = format! + ( +r#"derive : Former +item : {item}"#, + ); + diag::report_print( about, original_input, debug ); }; Ok( former_mutator_code ) @@ -102,14 +109,14 @@ where /// Generate documentation for the former. /// -fn doc_generate( stru : &syn::Ident ) -> ( String, String ) +fn doc_generate( item : &syn::Ident ) -> ( String, String ) { let doc_former_mod = format! ( r#" Implementation of former for [{}]. "#, - stru + item ); let doc_former_struct = format! @@ -120,7 +127,7 @@ Structure to form [{}]. Represents a forming entity designed to construct object This structure holds temporary storage and context during the formation process and utilizes a defined end strategy to finalize the object creation. "#, - stru + item ); ( doc_former_mod, doc_former_struct ) @@ -143,29 +150,23 @@ pub fn former( input : proc_macro::TokenStream ) -> Result< TokenStream > Err( err ) => return Err( err ), }; let has_debug = attr::has_debug( ast.attrs.iter() )?; - let struct_attrs = StructAttributes::from_attrs( ast.attrs.iter() )?; + let struct_attrs = ItemAttributes::from_attrs( ast.attrs.iter() )?; /* names */ let vis = &ast.vis; - 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 item = &ast.ident; + let former = format_ident!( "{item}Former" ); + let former_storage = format_ident!( "{item}FormerStorage" ); + let former_definition = format_ident!( "{item}FormerDefinition" ); + let former_definition_types = format_ident!( "{item}FormerDefinitionTypes" ); + let as_subformer = format_ident!( "{item}AsSubformer" ); + let as_subformer_end = format_ident!( "{item}AsSubformerEnd" ); 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. +Represents an end condition for former of [`${item}`], 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`. @@ -182,7 +183,7 @@ specific needs of the broader forming context. It mandates the implementation of let extra : macro_tools::syn::AngleBracketedGenericArguments = parse_quote! { - < (), #stru < #struct_generics_ty >, former::ReturnPreformed > + < (), #item < #struct_generics_ty >, former::ReturnPreformed > }; let former_definition_args = generic_args::merge( &generics.into_generic_args(), &extra.into() ).args; @@ -209,12 +210,12 @@ specific needs of the broader forming context. It mandates the implementation of Definition : former::FormerDefinition < Storage = #former_storage < #struct_generics_ty >, - Formed = #stru < #struct_generics_ty >, + Formed = #item < #struct_generics_ty >, >, Definition::Types : former::FormerDefinitionTypes < Storage = #former_storage < #struct_generics_ty >, - Formed = #stru < #struct_generics_ty >, + Formed = #item < #struct_generics_ty >, >, }; let extra = generic_params::merge( &generics, &extra.into() ); @@ -226,7 +227,7 @@ specific needs of the broader forming context. It mandates the implementation of let extra : macro_tools::GenericsWithWhere = parse_quote! { - < __Context = (), __Formed = #stru < #struct_generics_ty > > + < __Context = (), __Formed = #item < #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 ) @@ -238,7 +239,7 @@ specific needs of the broader forming context. It mandates the implementation of let extra : macro_tools::GenericsWithWhere = parse_quote! { - < __Context = (), __Formed = #stru < #struct_generics_ty >, __End = former::ReturnPreformed > + < __Context = (), __Formed = #item < #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 ) @@ -248,31 +249,29 @@ specific needs of the broader forming context. It mandates the implementation of /* struct attributes */ - let ( _doc_former_mod, doc_former_struct ) = doc_generate( stru ); + let ( _doc_former_mod, doc_former_struct ) = doc_generate( item ); let ( perform, perform_output, perform_generics ) = struct_attrs.performer()?; /* fields */ let fields = derive::named_fields( &ast )?; - let formed_fields : Vec< Result< FormerField< '_ > > > = fields + let formed_fields : Vec< _ > = fields .into_iter() .map( | field | { FormerField::from_syn( field, true, true ) }) - .collect(); - let formed_fields : Vec< _ > = process_results( formed_fields, | iter | iter.collect() )?; + .collect::< Result< _ > >()?; - let storage_fields : Vec< Result< FormerField< '_ > > > = struct_attrs + let storage_fields : Vec< _ > = struct_attrs .storage_fields() .iter() .map( | field | { - FormerField::from_syn( &field, true, false ) + FormerField::from_syn( field, true, false ) }) - .collect(); - let storage_fields : Vec< _ > = process_results( storage_fields, | iter | iter.collect() )?; + .collect::< Result< _ > >()?; let ( @@ -295,7 +294,8 @@ specific needs of the broader forming context. It mandates the implementation of field.storage_field_preform(), field.former_field_setter ( - &stru, + &item, + &original_input, &struct_generics_impl, &struct_generics_ty, &struct_generics_where, @@ -304,17 +304,21 @@ specific needs of the broader forming context. It mandates the implementation of &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 storage_field_preform : Vec< _ > = process_results( storage_field_preform, | iter | iter.collect() )?; + let storage_field_preform : Vec< _ > = storage_field_preform + .into_iter() + .collect::< Result< _ > >()?; let former_mutator_code = mutator ( + &item, + &original_input, &struct_attrs.mutator, &former_definition_types, &former_definition_types_generics_impl, @@ -328,7 +332,7 @@ specific needs of the broader forming context. It mandates the implementation of // = formed #[ automatically_derived ] - impl < #struct_generics_impl > #stru < #struct_generics_ty > + impl < #struct_generics_impl > #item < #struct_generics_ty > where #struct_generics_where { @@ -348,7 +352,7 @@ specific needs of the broader forming context. It mandates the implementation of // = entity to former impl< #struct_generics_impl Definition > former::EntityToFormer< Definition > - for #stru < #struct_generics_ty > + for #item < #struct_generics_ty > where Definition : former::FormerDefinition< Storage = #former_storage < #struct_generics_ty > >, #struct_generics_where @@ -357,7 +361,7 @@ specific needs of the broader forming context. It mandates the implementation of } impl< #struct_generics_impl > former::EntityToStorage - for #stru < #struct_generics_ty > + for #item < #struct_generics_ty > where #struct_generics_where { @@ -365,7 +369,7 @@ specific needs of the broader forming context. It mandates the implementation of } impl< #struct_generics_impl __Context, __Formed, __End > former::EntityToDefinition< __Context, __Formed, __End > - for #stru < #struct_generics_ty > + for #item < #struct_generics_ty > where __End : former::FormingEnd< #former_definition_types < #struct_generics_ty __Context, __Formed > >, #struct_generics_where @@ -375,7 +379,7 @@ specific needs of the broader forming context. It mandates the implementation of } impl< #struct_generics_impl __Context, __Formed > former::EntityToDefinitionTypes< __Context, __Formed > - for #stru < #struct_generics_ty > + for #item < #struct_generics_ty > where #struct_generics_where { @@ -390,7 +394,7 @@ specific needs of the broader forming context. It mandates the implementation of where #former_definition_types_generics_where { - // _phantom : core::marker::PhantomData< ( __Context, __Formed ) >, + // _phantom : ::core::marker::PhantomData< ( __Context, __Formed ) >, _phantom : #former_definition_types_phantom, } @@ -403,7 +407,7 @@ specific needs of the broader forming context. It mandates the implementation of { Self { - _phantom : core::marker::PhantomData, + _phantom : ::core::marker::PhantomData, } } } @@ -426,7 +430,7 @@ specific needs of the broader forming context. It mandates the implementation of where #former_definition_generics_where { - // _phantom : core::marker::PhantomData< ( __Context, __Formed, __End ) >, + // _phantom : ::core::marker::PhantomData< ( __Context, __Formed, __End ) >, _phantom : #former_definition_phantom, } @@ -439,7 +443,7 @@ specific needs of the broader forming context. It mandates the implementation of { Self { - _phantom : core::marker::PhantomData, + _phantom : ::core::marker::PhantomData, } } } @@ -497,7 +501,7 @@ specific needs of the broader forming context. It mandates the implementation of where #struct_generics_where { - type Preformed = #stru < #struct_generics_ty >; + type Preformed = #item < #struct_generics_ty >; } impl < #struct_generics_impl > former::StoragePreform @@ -505,14 +509,14 @@ specific needs of the broader forming context. It mandates the implementation of where #struct_generics_where { - // type Preformed = #stru < #struct_generics_ty >; + // type Preformed = #item < #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 > + let result = #item :: < #struct_generics_ty > { #( #storage_field_name )* // #( #storage_field_name, )* @@ -534,10 +538,10 @@ specific needs of the broader forming context. It mandates the implementation of 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 >, + 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 >, + pub on_end : ::core::option::Option< Definition::End >, } #[ automatically_derived ] @@ -577,8 +581,8 @@ specific needs of the broader forming context. It mandates the implementation of #[ inline( always ) ] pub fn begin ( - mut storage : core::option::Option< Definition::Storage >, - context : core::option::Option< Definition::Context >, + mut storage : ::core::option::Option< Definition::Storage >, + context : ::core::option::Option< Definition::Context >, on_end : < Definition as former::FormerDefinition >::End, ) -> Self @@ -601,8 +605,8 @@ specific needs of the broader forming context. It mandates the implementation of #[ inline( always ) ] pub fn begin_coercing< IntoEnd > ( - mut storage : core::option::Option< Definition::Storage >, - context : core::option::Option< Definition::Context >, + mut storage : ::core::option::Option< Definition::Storage >, + context : ::core::option::Option< Definition::Context >, on_end : IntoEnd, ) -> Self where @@ -651,8 +655,8 @@ specific needs of the broader forming context. It mandates the implementation of 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 > >, + Definition : former::FormerDefinition< Storage = #former_storage < #struct_generics_ty >, Formed = #item < #struct_generics_ty > >, + Definition::Types : former::FormerDefinitionTypes< Storage = #former_storage < #struct_generics_ty >, Formed = #item < #struct_generics_ty > >, #former_generics_where { @@ -704,8 +708,8 @@ specific needs of the broader forming context. It mandates the implementation of #[ inline( always ) ] fn former_begin ( - storage : core::option::Option< Definition::Storage >, - context : core::option::Option< Definition::Context >, + storage : ::core::option::Option< Definition::Storage >, + context : ::core::option::Option< Definition::Context >, on_end : Definition::End, ) -> Self @@ -769,7 +773,7 @@ specific needs of the broader forming context. It mandates the implementation of if has_debug { - let about = format!( "derive : Former\nstructure : {stru}" ); + let about = format!( "derive : Former\nstructure : {item}" ); diag::report_print( about, &original_input, &result ); } diff --git a/module/core/former_meta/src/derive_former/field.rs b/module/core/former_meta/src/derive_former/field.rs index a38cdead95..13a702b308 100644 --- a/module/core/former_meta/src/derive_former/field.rs +++ b/module/core/former_meta/src/derive_former/field.rs @@ -33,13 +33,14 @@ storage_field_optional storage_field_preform storage_field_name former_field_setter -subform_setter -container_setter scalar_setter +subform_entry_setter +subform_collection_setter scalar_setter_name -container_setter_name -subform_setter_name +subform_scalar_setter_name, +subform_collection_setter_name +subform_entry_setter_name scalar_setter_required */ @@ -75,7 +76,7 @@ scalar_setter_required /// /// 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 + /// Used for initializing a Collection, where on initialization all fields are None. User can alter them through builder pattern /// /// ### Basic use-case. of output /// @@ -102,7 +103,7 @@ scalar_setter_required /// /// Generate field of the former for a field of the structure /// - /// Used to generate a Container + /// Used to generate a Collection /// /// ### Basic use-case. of output /// @@ -183,7 +184,7 @@ scalar_setter_required let ident = self.ident; let ty = self.ty; let default : Option< &syn::Expr > = self.attrs.config.as_ref() - .and_then( | attr | attr.default.as_ref() ); + .and_then( | attr | attr.default.ref_internal() ); let tokens = if self.is_optional { @@ -306,7 +307,7 @@ scalar_setter_required /// /// 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, + /// types of setters based on the field attributes, such as scalar setters, collection setters, /// and subform setters. /// /// # Returns @@ -319,7 +320,7 @@ scalar_setter_required /// /// 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, + /// - **Collection Setters**: Generated when the field is annotated to behave as a collection, /// 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. @@ -329,7 +330,8 @@ scalar_setter_required pub fn former_field_setter ( &self, - stru : &syn::Ident, + item : &syn::Ident, + original_input : &proc_macro::TokenStream, 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 >, @@ -338,23 +340,47 @@ scalar_setter_required 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 ) > { + + // scalar setter let namespace_code = qt! {}; let setters_code = self.scalar_setter ( + item, former, former_storage, + original_input, ); - // container setter - let ( setters_code, namespace_code ) = if let Some( _ ) = &self.attrs.container + // subform scalar setter + let ( setters_code, namespace_code ) = if self.attrs.subform_scalar.is_some() + { + let ( setters_code2, namespace_code2 ) = self.subform_scalar_setter + ( + item, + former, + former_storage, + former_generics_ty, + struct_generics_impl, + struct_generics_ty, + struct_generics_where, + original_input, + )?; + ( qt! { #setters_code #setters_code2 }, qt! { #namespace_code #namespace_code2 } ) + } + else + { + ( setters_code, namespace_code ) + }; + + // subform collection setter + let ( setters_code, namespace_code ) = if let Some( _ ) = &self.attrs.subform_collection { - let ( setters_code2, namespace_code2 ) = self.container_setter + let ( setters_code2, namespace_code2 ) = self.subform_collection_setter ( - stru, + item, former, former_storage, former_generics_impl, @@ -369,18 +395,19 @@ scalar_setter_required ( setters_code, namespace_code ) }; - // subform setter - let ( setters_code, namespace_code ) = if self.attrs.subform.is_some() + // subform entry setter + let ( setters_code, namespace_code ) = if self.attrs.subform_entry.is_some() { - let ( setters_code2, namespace_code2 ) = self.subform_setter + let ( setters_code2, namespace_code2 ) = self.subform_entry_setter ( - stru, + item, former, former_storage, former_generics_ty, struct_generics_impl, struct_generics_ty, struct_generics_where, + original_input, )?; ( qt! { #setters_code #setters_code2 }, qt! { #namespace_code #namespace_code2 } ) } @@ -393,67 +420,493 @@ scalar_setter_required Ok( ( setters_code, namespace_code ) ) } - /// Generates setter functions for subforms within a container structure in a builder pattern. + /// + /// 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, + item : &syn::Ident, + former : &syn::Ident, + former_storage : &syn::Ident, + original_input : &proc_macro::TokenStream, + ) + -> 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().debug.value( false ) + { + let debug = format! + ( + r#" +impl< Definition > {former}< Definition > +where + Definition : former::FormerDefinition< Storage = {former_storage} >, +{{ + #[ inline ] + pub fn {field_ident}< Src >( mut self, src : Src ) -> Self + where + Src : ::core::convert::Into< {0} >, + {{ + debug_assert!( self.storage.{field_ident}.is_none() ); + self.storage.{field_ident} = ::core::option::Option::Some( ::core::convert::Into::into( src ) ); + self + }} +}} + "#, + format!( "{}", qt!{ #typ } ), + ); + let about = format! + ( +r#"derive : Former +item : {item} +field : {field_ident}"#, + ); + diag::report_print( about, original_input, debug ); + } + + 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 + } + } + + } + + /// + /// Generate a collection setter for the 'field_ident' with the 'setter_name' name. + /// + /// See `tests/inc/former_tests/subform_collection_manual.rs` for example of generated code. + /// + + #[ inline ] + pub fn subform_collection_setter + ( + &self, + item : &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.subform_collection.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 }; + + // example : `ParentSubformCollectionChildrenEnd` + let subform_collection_end = format_ident! + { + "{}SubformCollection{}End", + item, + field_ident.to_string().to_case( Case::Pascal ) + }; + + // example : `_children_subform_collection` + let subform_collection = format_ident! + { + "_{}_subform_collection", + field_ident + }; + // example : `former::VectorDefinition` + let subformer_definition = &attr.definition; + let subformer_definition = if subformer_definition.is_some() + { + qt! + { + #subformer_definition + < + #( #params, )* + Self, + Self, + #subform_collection_end< Definition >, + > + } + // former::VectorDefinition< String, Self, Self, Struct1SubformCollectionVec1End, > + } + else + { + qt! + { + < + #field_typ as former::EntityToDefinition< Self, Self, #subform_collection_end< Definition > > + >::Definition + } + // < Vec< String > as former::EntityToDefinition< Self, Self, Struct1SubformCollectionVec1End > >::Definition + }; + + let doc = format! + ( + "Collection setter for the '{}' field. Method {} unlike method {} accept custom collection subformer.", + field_ident, + subform_collection, + field_ident, + ); + + let setter1 = + qt! + { + + #[ doc = #doc ] + #[ inline( always ) ] + pub fn #subform_collection< Former2 >( self ) -> Former2 + where + Former2 : former::FormerBegin + < + #subformer_definition, + >, + #subformer_definition : former::FormerDefinition + < + // Storage : former::CollectionAdd< Entry = < #field_typ as former::Collection >::Entry >, + Storage = #field_typ, + Context = #former< #former_generics_ty >, + End = #subform_collection_end< Definition >, + >, + { + Former2::former_begin( None, Some( self ), #subform_collection_end::< Definition >::default() ) + } + + // #[ inline( always ) ] + // pub fn _hashset_1_assign< Former2 >( self ) -> Former2 + // where + // Former2 : former::FormerBegin + // < + // former::HashSetDefinition< String, Self, Self, Struct1SubformCollectionHashset1End< Definition > >, + // >, + // former::HashSetDefinition< String, Self, Self, Struct1SubformCollectionHashset1End< Definition > > : former::FormerDefinition + // < + // Storage : former::CollectionAdd< Entry = < collection_tools::HashSet< String > as former::Collection >::Entry >, + // Context = Struct1Former< Definition >, + // End = Struct1SubformCollectionHashset1End< Definition >, + // >, + // { + // Former2::former_begin( None, Some( self ), Struct1SubformCollectionHashset1End::< Definition >::default() ) + // } + + }; + + let setter_name = self.subform_collection_setter_name(); + let setter2 = if let Some( setter_name ) = setter_name + { + qt! + { + + #[ doc = #doc ] + #[ inline( always ) ] + pub fn #setter_name( self ) -> former::CollectionFormer:: + < + // ( #( #params, )* ), + < #field_typ as former::Collection >::Entry, + #subformer_definition, + > + where + #subformer_definition : former::FormerDefinition + < + // Storage : former::CollectionAdd< Entry = < #field_typ as former::Collection >::Entry >, + Storage = #field_typ, + Context = #former< #former_generics_ty >, + End = #subform_collection_end < Definition >, + >, + { + self.#subform_collection::< former::CollectionFormer:: + < + _, + _, + // ( #( #params, )* ), + // #subformer_definition, + > > () + } + + // #[ inline( always ) ] + // pub fn hashset_1( self ) -> former::CollectionFormer:: + // < + // String, + // former::HashSetDefinition< String, Self, Self, Struct1SubformCollectionHashset1End< Definition > >, + // > + // where + // former::HashSetDefinition< String, Self, Self, Struct1SubformCollectionHashset1End< Definition > > : former::FormerDefinition + // < + // Storage : former::CollectionAdd< Entry = < collection_tools::HashSet< String > as former::Collection >::Entry >, + // Context = Struct1Former< Definition >, + // End = Struct1SubformCollectionHashset1End< Definition >, + // >, + // { + // self._hashset_1_assign::< former::CollectionFormer:: + // < + // String, + // former::HashSetDefinition< String, Self, Self, Struct1SubformCollectionHashset1End< Definition > >, + // > > () + // } + + } + } + else + { + qt!{} + }; + + if attr.debug.value( false ) + { + let debug = format! + ( + r#" +/// The collection setter provides a collection setter that returns a CollectionFormer tailored for managing a collection of child entities. It employs a generic collection definition to facilitate operations on the entire collection, such as adding or updating elements. + +impl< Definition, > {former}< Definition, > +where + Definition : former::FormerDefinition< Storage = {former_storage} >, +{{ + + #[ inline( always ) ] + pub fn {field_ident}( self ) -> former::CollectionFormer:: + < + ( {0} ), + former::HashMapDefinition< {0} Self, Self, {subform_collection_end}< Definition >, > + // Replace `HashMapDefinition` with definition for your collection + > + {{ + self.{subform_collection}() + }} + +}} + "#, + format!( "{}", qt!{ #( #params, )* } ), + ); + let about = format! + ( +r#"derive : Former +item : {item} +field : {field_ident}"#, + ); + diag::report_print( about, original_input, debug ); + } + + let setters_code = qt! + { + #setter1 + #setter2 + }; + + // example : `former::VectorDefinition`` + let subformer_definition = self.attrs.subform_collection.as_ref().unwrap().definition.ref_internal(); + + let subform_collection_end_doc = format! + ( + r#" +A callback structure to manage the final stage of forming a `{0}` for the `{item}` collection. + +This callback is used to integrate the contents of a temporary `{0}` back into the original `{item}` former +after the subforming process is completed. It replaces the existing content of the `{field_ident}` field in `{item}` +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 = #subform_collection_end_doc ] + pub struct #subform_collection_end< Definition > + { + _phantom : core::marker::PhantomData< ( Definition, ) >, + } + + impl< Definition > Default + for #subform_collection_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 #subform_collection_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::CollectionAssign::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 ) ) + } + + /// Generates setter functions to subform entries of a collection. /// /// 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. + /// data within a nested collection structure like a `HashMap` or a `Vec`. The setters facilitate the addition or + /// modification of entries within the collection, directly from the parent former's context. /// - /// See `examples/subformer_subform_manual.rs` for example of generated code. + /// See `tests/inc/former_tests/subform_entry_manual.rs` for example of generated code. /// #[ inline ] - pub fn subform_setter + pub fn subform_entry_setter ( &self, - stru : &syn::Ident, + item : &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 >, + original_input : &proc_macro::TokenStream, ) -> 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 entry_typ : &syn::Type = typ::parameter_first( field_typ )?; + + let attr = self.attrs.subform_entry.as_ref().unwrap(); // let params = typ::type_parameters( &self.non_optional_ty, .. ); - // example : `child` - let setter_name = self.subform_setter_name(); + // example : `children` + let setter_name = self.subform_entry_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 : `ParentSubformEntryChildrenEnd` + let subform_entry_end = format_ident! + { + "{}SubformEntry{}End", + item, + field_ident.to_string().to_case( Case::Pascal ) + }; - // example : `_children_former` - let field_add_name = format!( "_{}_add", field_ident ); - let field_add = syn::Ident::new( &field_add_name, field_ident.span() ); + // example : `_children_subform_entry` + let subform_entry = format_ident! + { + "_{}_subform_entry", + field_ident + }; let doc = format! ( r#" -Initiates the addition of {field_ident} to the `{stru}` entity using a dedicated subformer. +Initiates the addition of {field_ident} to the `{item}` 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 +which is part of the `{item}` entity's construction. The subformer is set up with a specific end condition +handled by `{subform_entry_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}. +allowing for dynamic and flexible construction of the `{item}` entity's {field_ident}. "#, format!( "{}", qt!{ #field_typ } ), @@ -464,24 +917,24 @@ allowing for dynamic and flexible construction of the `{stru}` entity's {field_i #[ doc = #doc ] #[ inline( always ) ] - pub fn #field_add< Former2, Definition2 >( self ) -> Former2 + pub fn #subform_entry< Former2, Definition2 >( self ) -> Former2 where Definition2 : former::FormerDefinition < - End = #former_add_end< Definition >, - Storage = < < #field_typ as former::Container >::Val as former::EntityToStorage >::Storage, + End = #subform_entry_end< Definition >, + Storage = < < #field_typ as former::Collection >::Val as former::EntityToStorage >::Storage, Formed = Self, Context = Self, >, Definition2::Types : former::FormerDefinitionTypes < - Storage = < < #field_typ as former::Container >::Val as former::EntityToStorage >::Storage, + Storage = < < #field_typ as former::Collection >::Val as former::EntityToStorage >::Storage, Formed = Self, Context = Self, >, Former2 : former::FormerBegin< Definition2 >, { - Former2::former_begin( None, Some( self ), #former_add_end::default() ) + Former2::former_begin( None, Some( self ), #subform_entry_end::default() ) } }; @@ -492,12 +945,12 @@ allowing for dynamic and flexible construction of the `{stru}` entity's {field_i let doc = format! ( r#" -Provides a user-friendly interface to add an instancce of {field_ident} to the {stru}. +Provides a user-friendly interface to add an instancce of {field_ident} to the {item}. # 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}. +allowing for dynamic and flexible construction of the `{item}` entity's {field_ident}. "#, format!( "{}", qt!{ #field_typ } ), @@ -510,17 +963,17 @@ allowing for dynamic and flexible construction of the `{stru}` entity's {field_i #[ doc = #doc ] #[ inline( always ) ] pub fn #setter_name( self ) -> - < < #field_typ as former::Container >::Val as former::EntityToFormer + < < #field_typ as former::Collection >::Val as former::EntityToFormer < < - < #field_typ as former::Container >::Val as former::EntityToDefinition< Self, Self, #former_add_end < Definition > > + < #field_typ as former::Collection >::Val as former::EntityToDefinition< Self, Self, #subform_entry_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, _, >() + self.#subform_entry + ::< < < #field_typ as former::Collection >::Val as former::EntityToFormer< _ > >::Former, _, >() // ::< #former< _ >, _, >() } } @@ -529,7 +982,7 @@ allowing for dynamic and flexible construction of the `{stru}` entity's {field_i // pub fn child( self ) -> // ChildAsSubformer< Self, impl ChildAsSubformerEnd< Self > > // { - // self._children_add + // self._children_subform_entry // ::< < Child as former::EntityToFormer< _ > >::Former, _, >() // } @@ -539,48 +992,50 @@ allowing for dynamic and flexible construction of the `{stru}` entity's {field_i setters_code }; - if attr.hint + if attr.debug.value( false ) { - let hint = format! + let debug = 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 > +impl< Definition > {former}< Definition > where - Definition : former::FormerDefinition< Storage = {} >, + Definition : former::FormerDefinition< Storage = {former_storage} >, {{ #[ inline( always ) ] - pub fn {}( self ) -> ChildAsSubformer< Self, impl ChildAsSubformerEnd< Self > > + pub fn {field_ident}( self ) -> {0}AsSubformer< Self, impl {0}AsSubformerEnd< Self > > {{ - self.{}::< ChildFormer< _ >, _, >() + self.{subform_entry}::< {0}Former< _ >, _, >() }} - // Replace Child with name of type of element value. + // Replace {0} with name of type of entry value. }} "#, - former, - former_storage, - field_ident, - field_add_name, + format!( "{}", qt!{ #entry_typ } ), ); - println!( "{hint}" ); + let about = format! + ( +r#"derive : Former +item : {item} +field : {field_ident}"#, + ); + diag::report_print( about, original_input, debug ); } 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. +Implements the `FormingEnd` trait for `{subform_entry_end}` to handle the final +stage of the forming process for a `{item}` collection 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 +temporary state into their final state within the `{item}`'s storage. The function ensures +that the `{item}`'s {field_ident} storage is initialized if not already set, and then adds the preformed elements to this storage. # Type Parameters @@ -599,7 +1054,7 @@ preformed elements to this storage. # Returns Returns the updated `{former}` instance with newly added {field_ident}, completing the -formation process of the `{stru}`. +formation process of the `{item}`. "#, format!( "{}", qt!{ #field_typ } ), @@ -610,13 +1065,13 @@ formation process of the `{stru}`. { #[ doc = #doc ] - pub struct #former_add_end< Definition > + pub struct #subform_entry_end< Definition > { _phantom : core::marker::PhantomData< fn( Definition ) >, } impl< Definition > Default - for #former_add_end< Definition > + for #subform_entry_end< Definition > { #[ inline( always ) ] fn default() -> Self @@ -629,15 +1084,15 @@ formation process of the `{stru}`. } impl< #struct_generics_impl Types2, Definition > former::FormingEnd< Types2, > - for #former_add_end< Definition > + for #subform_entry_end< Definition > where Definition : former::FormerDefinition < - Storage = < #stru < #struct_generics_ty > as former::EntityToStorage >::Storage, + Storage = < #item < #struct_generics_ty > as former::EntityToStorage >::Storage, >, Types2 : former::FormerDefinitionTypes < - Storage = < < #field_typ as former::Container >::Val as former::EntityToStorage >::Storage, + Storage = < < #field_typ as former::Collection >::Val as former::EntityToStorage >::Storage, Formed = #former< #former_generics_ty >, Context = #former< #former_generics_ty >, >, @@ -659,10 +1114,10 @@ formation process of the `{stru}`. } if let Some( ref mut field ) = super_former.storage.#field_ident { - former::ContainerAdd::add + former::CollectionAdd::add ( field, - < < #field_typ as former::Container >::Val as former::ValToEntry< #field_typ > > + < < #field_typ as former::Collection >::Val as former::ValToEntry< #field_typ > > ::val_to_entry( former::StoragePreform::preform( substorage ) ), ); } @@ -676,279 +1131,250 @@ formation process of the `{stru}`. Ok( ( setters_code, namespace_code ) ) } + /// Generates setter functions to subform scalar and all corresponding helpers. /// - /// Generate a container setter for the 'field_ident' with the 'setter_name' name. - /// - /// See `examples/subformer_container_manual.rs` for example of generated code. + /// See `tests/inc/former_tests/subform_scalar_manual.rs` for example of generated code. #[ inline ] - pub fn container_setter + pub fn subform_scalar_setter ( &self, - stru : &syn::Ident, + item : &syn::Ident, former : &syn::Ident, - former_storage : &syn::Ident, - former_generics_impl : &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, + _former_storage : &syn::Ident, former_generics_ty : &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, - former_generics_where : &syn::punctuated::Punctuated< syn::WherePredicate, 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 >, 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() ); + let field_ident = self.ident; + let field_typ = self.non_optional_ty; + let attr = self.attrs.subform_scalar.as_ref().unwrap(); + // let params = typ::type_parameters( &self.non_optional_ty, .. ); - // example : `former::VectorDefinition` - let subformer_definition = &attr.definition; - let subformer_definition = if subformer_definition.is_some() + // example : `children` + let setter_name = self.subform_scalar_setter_name(); + + // example : `ParentSubformScalarChildrenEnd` + let subform_scalar_end = format_ident! { - qt! - { - #subformer_definition - < - #( #params, )* - Self, - Self, - #former_assign_end< Definition >, - > - } - // former::VectorDefinition< String, Self, Self, Struct1FormerAssignVec1End, > - } - else + "{}SubformScalar{}End", + item, + field_ident.to_string().to_case( Case::Pascal ) + }; + + // example : `_children_subform_scalar` + let subform_scalar = format_ident! { - qt! - { - < - #field_typ as former::EntityToDefinition< Self, Self, #former_assign_end< Definition > > - >::Definition - } - // < Vec< String > as former::EntityToDefinition< Self, Self, Struct1FormerAssignVec1End > >::Definition + "_{}_subform_scalar", + field_ident }; let doc = format! ( - "Container setter for the '{}' field. Method {} unlike method {} accept custom container subformer.", - field_ident, - field_assign_name, - field_ident, + r#" + +Initiates the scalar subformer for a `{0}` entity within a `{item}`. + +This function creates a subformer specifically for handling scalar values associated with a `{0}` entity, +leveraging a dedicated end structure to integrate the formed value seamlessly back into the `{item}`. + +## Type Parameters + +- `Former2`: Represents the specific former to be returned. +- `Definition2`: Defines the former's setup including its end action and storage specifics. + +## Returns + +- `Former2`: An instance of the former configured to handle the scalar formation of a `{0}`. + +This method prepares the forming context, ensuring that the subforming process for a scalar field in `{item}` +is properly initialized with all necessary configurations, including the default end action for integration. + +## Usage + +This function is typically called internally by a more user-friendly method that abstracts away the complex +generics, providing a cleaner interface for initiating subform operations on scalar fields. + + "#, + format!( "{}", qt!{ #field_typ } ), ); - let setter1 = - qt! + let setters_code = qt! { #[ doc = #doc ] #[ inline( always ) ] - pub fn #field_assign< Former2 >( self ) -> Former2 + pub fn #subform_scalar< Former2, Definition2 >( self ) -> + Former2 where - Former2 : former::FormerBegin + Definition2 : former::FormerDefinition < - #subformer_definition, + End = #subform_scalar_end< Definition >, + Storage = < #field_typ as former::EntityToStorage >::Storage, + Formed = Self, + Context = Self, >, - #subformer_definition : former::FormerDefinition + Definition2::Types : former::FormerDefinitionTypes < - // Storage : former::ContainerAdd< Entry = < #field_typ as former::Container >::Entry >, - Storage = #field_typ, - Context = #former< #former_generics_ty >, - End = #former_assign_end< Definition >, + Storage = < #field_typ as former::EntityToStorage >::Storage, + Formed = Self, + Context = Self, >, + Former2 : former::FormerBegin< Definition2 >, { - Former2::former_begin( None, Some( self ), #former_assign_end::< Definition >::default() ) + Former2::former_begin( None, Some( self ), #subform_scalar_end::default() ) } // #[ inline( always ) ] - // pub fn _hashset_1_assign< Former2 >( self ) -> Former2 + // pub fn _child_scalar_subformer< Former2, Definition2 >( self ) -> + // Former2 // where - // Former2 : former::FormerBegin + // Definition2 : former::FormerDefinition // < - // former::HashSetDefinition< String, Self, Self, Struct1FormerAssignHashset1End< Definition > >, + // End = ParentFormerSubformScalarChildEnd< Definition >, + // Storage = < Child as former::EntityToStorage >::Storage, + // Formed = Self, + // Context = Self, // >, - // former::HashSetDefinition< String, Self, Self, Struct1FormerAssignHashset1End< Definition > > : former::FormerDefinition + // Definition2::Types : former::FormerDefinitionTypes // < - // Storage : former::ContainerAdd< Entry = < collection_tools::HashSet< String > as former::Container >::Entry >, - // Context = Struct1Former< Definition >, - // End = Struct1FormerAssignHashset1End< Definition >, + // Storage = < Child as former::EntityToStorage >::Storage, + // Formed = Self, + // Context = Self, // >, + // Former2 : former::FormerBegin< Definition2 >, // { - // Former2::former_begin( None, Some( self ), Struct1FormerAssignHashset1End::< Definition >::default() ) + // Former2::former_begin( None, Some( self ), ParentFormerSubformScalarChildEnd::default() ) // } }; - let setter_name = self.container_setter_name(); - let setter2 = if let Some( setter_name ) = setter_name + let setters_code = if attr.setter() { + + let doc = format! + ( + r#" +Provides a user-friendly interface to begin subforming a scalar `{0}` field within a `{item}`. + +This method abstracts the underlying complex generics involved in setting up the former, simplifying the +user interaction needed to initiate the subform process for a scalar field associated with a `{0}`. + +This method utilizes the more generic `{subform_scalar}` method to set up and return the subformer, +providing a straightforward and type-safe interface for client code. It encapsulates details about the specific +former and end action types, ensuring a seamless developer experience when forming parts of a `{item}`. + + "#, + format!( "{}", qt!{ #field_typ } ), + ); + qt! { + #setters_code #[ 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 + pub fn #setter_name( self ) -> + < #field_typ as former::EntityToFormer < - // Storage : former::ContainerAdd< Entry = < #field_typ as former::Container >::Entry >, - Storage = #field_typ, - Context = #former< #former_generics_ty >, - End = #former_assign_end < Definition >, - >, + < + #field_typ as former::EntityToDefinition< Self, Self, #subform_scalar_end < Definition > > + >::Definition, + > + >::Former { - self.#field_assign::< former::ContainerFormer:: - < - _, - _, - // ( #( #params, )* ), - // #subformer_definition, - > > () + self.#subform_scalar + ::< < #field_typ as former::EntityToFormer< _ > >::Former, _, >() } // #[ 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 >, - // >, + // pub fn child( self ) -> + // ChildAsSubformer< Self, impl ChildAsSubformerEnd< Self > > // { - // self._hashset_1_assign::< former::ContainerFormer:: - // < - // String, - // former::HashSetDefinition< String, Self, Self, Struct1FormerAssignHashset1End< Definition > >, - // > > () + // self._child_scalar_subformer + // ::< < Child as former::EntityToFormer< _ > >::Former, _, >() // } } + } else { - qt!{} + setters_code }; - if attr.hint + if attr.debug.value( false ) { - let hint = format! + let debug = format! ( r#" +/// Extends `{former}` to include a method that initializes and configures a subformer for the '{field_ident}' field. +/// This function demonstrates the dynamic addition of a named {field_ident}, leveraging a subformer to specify detailed properties. -/// 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, > +impl< Definition > {former}< Definition > where - Definition : former::FormerDefinition< Storage = {} >, + Definition : former::FormerDefinition< Storage = < {item} as former::EntityToStorage >::Storage >, {{ - #[ inline( always ) ] - pub fn {}( self ) -> former::ContainerFormer:: - < - ( {} ), - former::HashMapDefinition< {} Self, Self, {}< Definition >, > - // Replace `HashMapDefinition` with definition for your container - > + pub fn {field_ident}( self, name : &str ) -> {0}AsSubformer< Self, impl {0}AsSubformerEnd< Self > > {{ - self.{}() + self._{field_ident}_subform_scalar::< {0}Former< _ >, _, >().name( name ) }} - }} - "#, - former, - former_storage, - field_ident, - format!( "{}", qt!{ #( #params, )* } ), - format!( "{}", qt!{ #( #params, )* } ), - former_assign_end, - field_assign, + format!( "{}", qt!{ #field_typ } ), ); let about = format! ( r#"derive : Former -structure : {stru} +item : {item} field : {field_ident}"#, ); - diag::report_print( about, original_input, hint ); + diag::report_print( about, original_input, debug ); } - 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! + let 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. +Represents the endpoint for the forming process of a scalar field managed by a subformer within a `{item}` entity. + +This structure is a critical component of the forming process when using a subform scalar setter. It handles +the finalization of the scalar field's value that has been configured through its dedicated subformer. +Essentially, this end action integrates the individually formed scalar value back into the parent structure. + +## Type Parameters + +- `Definition`: The type that defines the former setup for the `{item}` entity, influencing storage and behavior during forming. + +## Parameters of `call` + +- `substorage`: Storage type specific to the `{0}`, containing the newly formed scalar value. +- `super_former`: An optional context of the `{former}`, which will receive the value. The function ensures + that this context is not `None` and inserts the formed value into the designated field within `{item}`'s storage. + "#, 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! + let namespace_code = qt! { - #[ doc = #former_assign_end_doc ] - pub struct #former_assign_end< Definition > + #[ doc = #doc ] + pub struct #subform_scalar_end< Definition > { - _phantom : core::marker::PhantomData< ( Definition, ) >, + _phantom : core::marker::PhantomData< fn( Definition ) >, } impl< Definition > Default - for #former_assign_end< Definition > + for #subform_scalar_end< Definition > { - #[ inline( always ) ] fn default() -> Self { @@ -957,163 +1383,134 @@ with the new content generated during the subforming process. _phantom : core::marker::PhantomData, } } - } - #[ automatically_derived ] - impl< #former_generics_impl > former::FormingEnd - < - // VectorDefinitionTypes - #subformer_definition_types, - > - for #former_assign_end< Definition > + impl< #struct_generics_impl Types2, Definition > former::FormingEnd< Types2, > + for #subform_scalar_end< Definition > where - #former_generics_where + Definition : former::FormerDefinition + < + Storage = < #item < #struct_generics_ty > as former::EntityToStorage >::Storage, + >, + Types2 : former::FormerDefinitionTypes + < + Storage = < #field_typ as former::EntityToStorage >::Storage, + Formed = #former< #former_generics_ty >, + Context = #former< #former_generics_ty >, + >, + #struct_generics_where { #[ inline( always ) ] fn call ( &self, - storage : #field_typ, - super_former : Option< #former< #former_generics_ty > >, + substorage : Types2::Storage, + super_former : core::option::Option< Types2::Context >, ) - -> #former< #former_generics_ty > + -> Types2::Formed { 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 ); - } + debug_assert!( super_former.storage.#field_ident.is_none() ); + super_former.storage.#field_ident = Some( ::core::convert::Into::into( former::StoragePreform::preform( substorage ) ) ); super_former } } - }; +// pub struct ParentFormerSubformScalarChildEnd< Definition > +// { +// _phantom : core::marker::PhantomData< fn( Definition ) >, +// } +// +// impl< Definition > Default +// for ParentFormerSubformScalarChildEnd< Definition > +// { +// #[ inline( always ) ] +// fn default() -> Self +// { +// Self +// { +// _phantom : core::marker::PhantomData, +// } +// } +// } +// +// impl< Types2, Definition > former::FormingEnd< Types2, > +// for ParentFormerSubformScalarChildEnd< Definition > +// where +// Definition : former::FormerDefinition +// < +// Storage = < Parent as former::EntityToStorage >::Storage, +// >, +// Types2 : former::FormerDefinitionTypes +// < +// Storage = < Child 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(); +// debug_assert!( super_former.storage.child.is_none() ); +// super_former.storage.child = Some( ::core::convert::Into::into( former::StoragePreform::preform( substorage ) ) ); +// super_former +// } +// } - // tree_print!( r.as_ref().unwrap() ); - let namespace_code = r; + }; + // tree_print!( setters_code.as_ref().unwrap() ); 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 + /// Get name of scalar setter. + pub fn scalar_setter_name( &self ) -> &syn::Ident { - 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! + if let Some( ref attr ) = self.attrs.scalar { - #[ doc = #doc ] - #[ inline ] - pub fn #setter_name< Src >( mut self, src : Src ) -> Self - where - Src : ::core::convert::Into< #typ >, + if let Some( ref name ) = attr.name.ref_internal() { - debug_assert!( self.storage.#field_ident.is_none() ); - self.storage.#field_ident = ::core::option::Option::Some( ::core::convert::Into::into( src ) ); - self + return name } } + return &self.ident; } - /// Get name of scalar setter. - pub fn scalar_setter_name( &self ) -> &syn::Ident + /// Get name of setter for subform scalar if such setter should be generated. + pub fn subform_scalar_setter_name( &self ) -> Option< &syn::Ident > { - if let Some( ref attr ) = self.attrs.scalar + if let Some( ref attr ) = self.attrs.subform_scalar { - if let Some( ref name ) = attr.name + if attr.setter() { - return name + if let Some( ref name ) = attr.name.ref_internal() + { + return Some( &name ) + } + else + { + return Some( &self.ident ) + } } } - return &self.ident; + return None; } - /// Get name of setter for container if such setter should be generated. - pub fn container_setter_name( &self ) -> Option< &syn::Ident > + /// Get name of setter for collection if such setter should be generated. + pub fn subform_collection_setter_name( &self ) -> Option< &syn::Ident > { - - if let Some( ref attr ) = self.attrs.container + if let Some( ref attr ) = self.attrs.subform_collection { if attr.setter() { - if let Some( ref name ) = attr.name + if let Some( ref name ) = attr.name.ref_internal() { return Some( &name ) } @@ -1123,19 +1520,17 @@ where } } } - return None; } /// Get name of setter for subform if such setter should be generated. - pub fn subform_setter_name( &self ) -> Option< &syn::Ident > + pub fn subform_entry_setter_name( &self ) -> Option< &syn::Ident > { - - if let Some( ref attr ) = self.attrs.subform + if let Some( ref attr ) = self.attrs.subform_entry { if attr.setter() { - if let Some( ref name ) = attr.name + if let Some( ref name ) = attr.name.as_ref() { return Some( &name ) } @@ -1145,18 +1540,17 @@ where } } } - return None; } - /// Is scalar setter required. Does not if container of subformer setter requested. + /// Is scalar setter required. Does not if collection 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 let Some( setter ) = attr.setter.internal() { if setter == false { @@ -1164,18 +1558,23 @@ where } explicit = true; } - if let Some( ref _name ) = attr.name + if let Some( ref _name ) = attr.name.ref_internal() { explicit = true; } } - if self.attrs.container.is_some() && !explicit + if self.attrs.subform_scalar.is_some() && !explicit + { + return false; + } + + if self.attrs.subform_collection.is_some() && !explicit { return false; } - if self.attrs.subform.is_some() && !explicit + if self.attrs.subform_entry.is_some() && !explicit { return false; } diff --git a/module/core/former_meta/src/derive_former/field_attrs.rs b/module/core/former_meta/src/derive_former/field_attrs.rs index 5aea94b80d..0dd3cf3edc 100644 --- a/module/core/former_meta/src/derive_former/field_attrs.rs +++ b/module/core/former_meta/src/derive_former/field_attrs.rs @@ -1,110 +1,116 @@ +//! Attributes of a field. use super::*; -use macro_tools::{ attr, Result }; +use macro_tools:: +{ + Result, + AttributeComponent, + AttributePropertyComponent, + AttributePropertyOptionalBoolean, + AttributePropertyOptionalSyn, + AttributePropertyOptionalSingletone, +}; +use former_types::{ Assign, OptionExt }; /// /// Attributes of a field. /// +#[ derive( Debug, Default ) ] pub struct FieldAttributes { + /// Configuration attribute for a field. pub config : Option< AttributeConfig >, + + /// Scalar setter attribute for a field. pub scalar : Option< AttributeScalarSetter >, - pub container : Option< AttributeContainerSetter >, - pub subform : Option< AttributeSubformSetter >, + + /// Subform scalar setter attribute for a field. + pub subform_scalar : Option< AttributeSubformScalarSetter >, + + /// Subform collection setter attribute for a field. + pub subform_collection : Option< AttributeSubformCollectionSetter >, + + /// Subform entry setter attribute for a field. + pub subform_entry : Option< AttributeSubformEntrySetter >, } impl FieldAttributes { - // fn from_attrs( attributes : & Vec< syn::Attribute > ) -> Result< Self > + + /// Creates an instance of `FieldAttributes` from a list of attributes. + /// + /// # Parameters + /// + /// * `attrs`: An iterator over references to `syn::Attribute`. + /// + /// # Returns + /// + /// * `Result< Self >`: A result containing an instance of `FieldAttributes` on success, + /// or a `syn::Error` on failure. + /// + /// This function processes each attribute in the provided iterator and assigns the + /// appropriate attribute type to the respective field in the `FieldAttributes` struct. + /// pub fn from_attrs< 'a >( attrs : impl Iterator< Item = &'a syn::Attribute > ) -> Result< Self > { - let mut config = None; - let mut scalar = None; - let mut container = None; - let mut subform = None; + let mut result = Self::default(); + // Known attributes for error reporting + let known_attributes = const_format::concatcp! + ( + "Known attributes are : ", + "debug", + ", ", AttributeConfig::KEYWORD, + ", ", AttributeScalarSetter::KEYWORD, + ", ", AttributeSubformScalarSetter::KEYWORD, + ", ", AttributeSubformCollectionSetter::KEYWORD, + ", ", AttributeSubformEntrySetter::KEYWORD, + ".", + ); + + // Helper closure to create a syn::Error for unknown attributes + let error = | attr : &syn::Attribute | -> syn::Error + { + syn_err! + ( + attr, + "Expects an attribute of format `#[ attribute( key1 = val1, key2 = val2 ) ]`\n {known_attributes}\n But got:\n `{}`", + qt!{ #attr } + ) + }; + + // Iterate over the provided attributes for attr in attrs { - let key_ident = attr.path().get_ident() - .ok_or_else( || syn_err!( attr, "Expects an attribute of format #[ attribute( val ) ], but got:\n {}", qt!{ #attr } ) )?; + // Get the attribute key as a string + let key_ident = attr.path().get_ident().ok_or_else( || error( attr ) )?; let key_str = format!( "{}", key_ident ); - if attr::is_standard( &key_str ) - { - continue; - } + // // Skip standard attributes + // if attr::is_standard( &key_str ) + // { + // continue; + // } + // attributes does not have to be known + // Match the attribute key and assign to the appropriate field 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 } ) ); - } + AttributeConfig::KEYWORD => result.assign( AttributeConfig::from_meta( attr )? ), + AttributeScalarSetter::KEYWORD => result.assign( AttributeScalarSetter::from_meta( attr )? ), + AttributeSubformScalarSetter::KEYWORD => result.assign( AttributeSubformScalarSetter::from_meta( attr )? ), + AttributeSubformCollectionSetter::KEYWORD => result.assign( AttributeSubformCollectionSetter::from_meta( attr )? ), + AttributeSubformEntrySetter::KEYWORD => result.assign( AttributeSubformEntrySetter::from_meta( attr )? ), + "debug" => {}, + _ => {}, + // _ => return Err( error( attr ) ), + // attributes does not have to be known } } - Ok( FieldAttributes { config, scalar, container, subform } ) + Ok( result ) } + } /// @@ -113,24 +119,98 @@ impl FieldAttributes /// `#[ default( 13 ) ]` /// +#[ derive( Debug, Default ) ] pub struct AttributeConfig { - /// Default value to use for the field. - pub default : Option< syn::Expr >, + /// Default value to use for a field. + pub default : AttributePropertyDefault, + +} + +impl AttributeComponent for AttributeConfig +{ + + const KEYWORD : &'static str = "former"; + + fn from_meta( attr : &syn::Attribute ) -> Result< Self > + { + match attr.meta + { + syn::Meta::List( ref meta_list ) => + { + syn::parse2::< AttributeConfig >( meta_list.tokens.clone() ) + }, + syn::Meta::Path( ref _path ) => + { + syn::parse2::< AttributeConfig >( Default::default() ) + }, + _ => return_syn_err!( attr, "Expects an attribute of format #[ former( default = 13 ) ].\nGot: {}", qt!{ #attr } ), + } + } } -impl AttributeConfig +impl< IntoT > Assign< AttributeConfig, IntoT > for FieldAttributes +where + IntoT : Into< AttributeConfig >, { + #[ inline( always ) ] + fn assign( &mut self, component : IntoT ) + { + let component : AttributeConfig = component.into(); + self.config.option_assign( component ); + } +} + +impl< IntoT > Assign< AttributeConfig, IntoT > for AttributeConfig +where + IntoT : Into< AttributeConfig >, +{ + #[ inline( always ) ] + fn assign( &mut self, component : IntoT ) + { + let component = component.into(); + self.default.assign( component.default ); + } +} + +impl< IntoT > Assign< AttributePropertyDefault, IntoT > for AttributeConfig +where + IntoT : Into< AttributePropertyDefault >, +{ + #[ inline( always ) ] + fn assign( &mut self, component : IntoT ) + { + // panic!( "" ); + self.default.assign( component.into() ); + } } impl syn::parse::Parse for AttributeConfig { fn parse( input : syn::parse::ParseStream< '_ > ) -> syn::Result< Self > { - let mut default : Option< syn::Expr > = None; - // let mut only_storage : Option< bool > = None; + let mut result = Self::default(); + + let error = | ident : &syn::Ident | -> syn::Error + { + let known = const_format::concatcp! + ( + "Known entries of attribute ", AttributeConfig::KEYWORD, " are : ", + AttributePropertyDefault::KEYWORD, + ".", + ); + syn_err! + ( + ident, + r#"Expects an attribute of format '#[ former( default = 13 ) ]' + {known} + But got: '{}' +"#, + qt!{ #ident } + ) + }; while !input.is_empty() { @@ -138,20 +218,178 @@ impl syn::parse::Parse for AttributeConfig if lookahead.peek( syn::Ident ) { let ident : syn::Ident = input.parse()?; - if ident == "default" + match ident.to_string().as_str() { - input.parse::< syn::Token![ = ] >()?; - default = Some( input.parse()? ); + AttributePropertyDefault::KEYWORD => result.assign( AttributePropertyDefault::parse( input )? ), + _ => return Err( error( &ident ) ), } - else + } + else + { + return Err( lookahead.error() ); + } + + // Optional comma handling + if input.peek( syn::Token![ , ] ) + { + input.parse::< syn::Token![ , ] >()?; + } + } + + Ok( result ) + } +} + +#[ derive( Debug, Default ) ] +pub struct AttributeScalarSetter +{ + /// Optional identifier for naming the setter. + pub name : AttributePropertyName, + /// Controls the generation of a setter method. If false, a setter method is not generated. + pub setter : AttributePropertySetter, + /// Specifies whether to provide a sketch of the subform setter as a hint. + /// Defaults to `false`, which means no hint is provided unless explicitly requested. + pub debug : AttributePropertyDebug, +} + +impl AttributeScalarSetter +{ + + /// Should setter be generated or not? + #[ allow( dead_code ) ] + pub fn setter( &self ) -> bool + { + self.setter.is_none() || self.setter.unwrap() + } + +} + +impl AttributeComponent for AttributeScalarSetter +{ + + const KEYWORD : &'static str = "scalar"; + + fn from_meta( attr : &syn::Attribute ) -> Result< Self > + { + match attr.meta + { + syn::Meta::List( ref meta_list ) => + { + syn::parse2::< AttributeScalarSetter >( meta_list.tokens.clone() ) + }, + syn::Meta::Path( ref _path ) => + { + syn::parse2::< AttributeScalarSetter >( Default::default() ) + }, + _ => return_syn_err!( attr, "Expects an attribute of format `#[ scalar( setter = false ) ]` or `#[ scalar( setter = true, name = my_name ) ]`. \nGot: {}", qt!{ #attr } ), + } + } + +} + +impl< IntoT > Assign< AttributeScalarSetter, IntoT > for FieldAttributes +where + IntoT : Into< AttributeScalarSetter >, +{ + #[ inline( always ) ] + fn assign( &mut self, component : IntoT ) + { + let component = component.into(); + self.scalar.option_assign( component ); + } +} + +impl< IntoT > Assign< AttributeScalarSetter, IntoT > for AttributeScalarSetter +where + IntoT : Into< AttributeScalarSetter >, +{ + #[ inline( always ) ] + fn assign( &mut self, component : IntoT ) + { + let component = component.into(); + self.name.assign( component.name ); + self.setter.assign( component.setter ); + self.debug.assign( component.debug ); + } +} + +impl< IntoT > Assign< AttributePropertyName, IntoT > for AttributeScalarSetter +where + IntoT : Into< AttributePropertyName >, +{ + #[ inline( always ) ] + fn assign( &mut self, component : IntoT ) + { + self.name = component.into(); + } +} + +impl< IntoT > Assign< AttributePropertySetter, IntoT > for AttributeScalarSetter +where + IntoT : Into< AttributePropertySetter >, +{ + #[ inline( always ) ] + fn assign( &mut self, component : IntoT ) + { + self.setter = component.into(); + } +} + +impl< IntoT > Assign< AttributePropertyDebug, IntoT > for AttributeScalarSetter +where + IntoT : Into< AttributePropertyDebug >, +{ + #[ inline( always ) ] + fn assign( &mut self, component : IntoT ) + { + self.debug = component.into(); + } +} + +impl syn::parse::Parse for AttributeScalarSetter +{ + fn parse( input : syn::parse::ParseStream< '_ > ) -> syn::Result< Self > + { + let mut result = Self::default(); + + let error = | ident : &syn::Ident | -> syn::Error + { + let known = const_format::concatcp! + ( + "Known entries of attribute ", AttributeScalarSetter::KEYWORD, " are : ", + AttributePropertyName::KEYWORD, + ", ", AttributePropertySetter::KEYWORD, + ", ", AttributePropertyDebug::KEYWORD, + ".", + ); + syn_err! + ( + ident, + r#"Expects an attribute of format '#[ scalar( name = myName, setter = true ) ]' + {known} + But got: '{}' +"#, + qt!{ #ident } + ) + }; + + while !input.is_empty() + { + let lookahead = input.lookahead1(); + if lookahead.peek( syn::Ident ) + { + let ident : syn::Ident = input.parse()?; + match ident.to_string().as_str() { - return Err( syn::Error::new_spanned( &ident, format!( "Unexpected identifier '{}'. Expected 'default'. For example: `former( default = 13 )`", ident ) ) ); + AttributePropertyName::KEYWORD => result.assign( AttributePropertyName::parse( input )? ), + AttributePropertySetter::KEYWORD => result.assign( AttributePropertySetter::parse( input )? ), + AttributePropertyDebug::KEYWORD => result.assign( AttributePropertyDebug::from( true ) ), + _ => return Err( error( &ident ) ), } } - else { - return Err( syn::Error::new( input.span(), "Expected 'default'. For example: `former( default = 13 )`" ) ); + return Err( lookahead.error() ); } // Optional comma handling @@ -161,7 +399,7 @@ impl syn::parse::Parse for AttributeConfig } } - Ok( Self { default } ) + Ok( result ) } } @@ -177,19 +415,20 @@ impl syn::parse::Parse for AttributeConfig /// ``` /// -pub struct AttributeScalarSetter +#[ derive( Debug, Default ) ] + +pub struct AttributeSubformScalarSetter { /// Optional identifier for naming the setter. - pub name : Option< syn::Ident >, + pub name : AttributePropertyName, /// Controls the generation of a setter method. If false, a setter method is not generated. - pub setter : Option< bool >, + pub setter : AttributePropertySetter, /// Specifies whether to provide a sketch of the subform setter as a hint. /// Defaults to `false`, which means no hint is provided unless explicitly requested. - pub hint : bool, + pub debug : AttributePropertyDebug, } -#[ allow( dead_code ) ] -impl AttributeScalarSetter +impl AttributeSubformScalarSetter { /// Should setter be generated or not? @@ -200,13 +439,114 @@ impl AttributeScalarSetter } -impl syn::parse::Parse for AttributeScalarSetter +impl AttributeComponent for AttributeSubformScalarSetter +{ + + const KEYWORD : &'static str = "subform_scalar"; + + fn from_meta( attr : &syn::Attribute ) -> Result< Self > + { + match attr.meta + { + syn::Meta::List( ref meta_list ) => + { + syn::parse2::< AttributeSubformScalarSetter >( meta_list.tokens.clone() ) + }, + syn::Meta::Path( ref _path ) => + { + syn::parse2::< AttributeSubformScalarSetter >( Default::default() ) + }, + _ => return_syn_err!( attr, "Expects an attribute of format `#[ subform_scalar( setter = false ) ]` or `#[ subform_scalar( setter = true, name = my_name ) ]`. \nGot: {}", qt!{ #attr } ), + } + } + +} + +impl< IntoT > Assign< AttributeSubformScalarSetter, IntoT > for FieldAttributes +where + IntoT : Into< AttributeSubformScalarSetter >, +{ + #[ inline( always ) ] + fn assign( &mut self, component : IntoT ) + { + let component = component.into(); + self.subform_scalar.option_assign( component ); + } +} + +impl< IntoT > Assign< AttributeSubformScalarSetter, IntoT > for AttributeSubformScalarSetter +where + IntoT : Into< AttributeSubformScalarSetter >, +{ + #[ inline( always ) ] + fn assign( &mut self, component : IntoT ) + { + let component = component.into(); + self.name.assign( component.name ); + self.setter.assign( component.setter ); + self.debug.assign( component.debug ); + } +} + +impl< IntoT > Assign< AttributePropertyName, IntoT > for AttributeSubformScalarSetter +where + IntoT : Into< AttributePropertyName >, +{ + #[ inline( always ) ] + fn assign( &mut self, component : IntoT ) + { + self.name = component.into(); + } +} + +impl< IntoT > Assign< AttributePropertySetter, IntoT > for AttributeSubformScalarSetter +where + IntoT : Into< AttributePropertySetter >, +{ + #[ inline( always ) ] + fn assign( &mut self, component : IntoT ) + { + self.setter = component.into(); + } +} + +impl< IntoT > Assign< AttributePropertyDebug, IntoT > for AttributeSubformScalarSetter +where + IntoT : Into< AttributePropertyDebug >, +{ + #[ inline( always ) ] + fn assign( &mut self, component : IntoT ) + { + self.debug = component.into(); + } +} + +impl syn::parse::Parse for AttributeSubformScalarSetter { fn parse( input : syn::parse::ParseStream< '_ > ) -> syn::Result< Self > { - let mut name : Option< syn::Ident > = None; - let mut setter : Option< bool > = None; - let mut hint = false; + let mut result = Self::default(); + + let error = | ident : &syn::Ident | -> syn::Error + { + let known = const_format::concatcp! + ( + "Known entries of attribute ", AttributeSubformScalarSetter::KEYWORD, " are : ", + AttributePropertyName::KEYWORD, + ", ", AttributePropertySetter::KEYWORD, + ", ", AttributePropertyDebug::KEYWORD, + ".", + ); + syn_err! + ( + ident, + r#"Expects an attribute of format '#[ subform_scalar( name = myName, setter = true ) ]' + {known} + But got: '{}' +"#, + qt!{ #ident } + ) + }; while !input.is_empty() { @@ -214,48 +554,34 @@ impl syn::parse::Parse for AttributeScalarSetter 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" + match ident.to_string().as_str() { - 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 ) ) ); + AttributePropertyName::KEYWORD => result.assign( AttributePropertyName::parse( input )? ), + AttributePropertySetter::KEYWORD => result.assign( AttributePropertySetter::parse( input )? ), + AttributePropertyDebug::KEYWORD => result.assign( AttributePropertyDebug::from( true ) ), + _ => return Err( error( &ident ) ), } } else { - return Err( syn::Error::new( input.span(), "Expected 'name', 'setter', or 'definition' identifier. For example: `scalar( name = myName, setter = true )`" ) ); + return Err( lookahead.error() ); } // Optional comma handling - if input.peek( syn::Token![,] ) + if input.peek( syn::Token![ , ] ) { - input.parse::< syn::Token![,] >()?; + input.parse::< syn::Token![ , ] >()?; } } - Ok( Self { name, setter, hint } ) + Ok( result ) } } -/// Represents an attribute for configuring container setter generation. +/// Represents an attribute for configuring collection setter generation. /// /// This struct is part of a meta-programming approach to enable detailed configuration of nested structs or collections such as `Vec< E >, HashMap< K, E >` and so on. -/// It allows the customization of setter methods and the specification of the container's behavior through meta attributes. +/// It allows the customization of setter methods and the specification of the collection's behavior through meta attributes. /// /// ## Example Input /// @@ -265,20 +591,21 @@ impl syn::parse::Parse for AttributeScalarSetter /// ``` /// -pub struct AttributeContainerSetter +#[ derive( Debug, Default ) ] +pub struct AttributeSubformCollectionSetter { /// Optional identifier for naming the setter. - pub name : Option< syn::Ident >, + pub name : AttributePropertyName, /// Controls the generation of a setter method. If false, a setter method is not generated. - pub setter : Option< bool >, - /// Definition of the container former to use, e.g., `former::VectorFormer`. - pub definition : Option< syn::Type >, + pub setter : AttributePropertySetter, /// Specifies whether to provide a sketch of the subform setter as a hint. /// Defaults to `false`, which means no hint is provided unless explicitly requested. - pub hint : bool, + pub debug : AttributePropertyDebug, + /// Definition of the collection former to use, e.g., `former::VectorFormer`. + pub definition : AttributePropertyDefinition, } -impl AttributeContainerSetter +impl AttributeSubformCollectionSetter { /// Should setter be generated or not? @@ -289,14 +616,127 @@ impl AttributeContainerSetter } -impl syn::parse::Parse for AttributeContainerSetter +impl AttributeComponent for AttributeSubformCollectionSetter +{ + + const KEYWORD : &'static str = "subform_collection"; + + fn from_meta( attr : &syn::Attribute ) -> Result< Self > + { + match attr.meta + { + syn::Meta::List( ref meta_list ) => + { + syn::parse2::< AttributeSubformCollectionSetter >( meta_list.tokens.clone() ) + }, + syn::Meta::Path( ref _path ) => + { + syn::parse2::< AttributeSubformCollectionSetter >( Default::default() ) + }, + _ => return_syn_err!( attr, "Expects an attribute of format `#[ subform_collection ]` or `#[ subform_collection( definition = former::VectorDefinition ) ]` if you want to use default collection defition. \nGot: {}", qt!{ #attr } ), + } + } + +} + +impl< IntoT > Assign< AttributeSubformCollectionSetter, IntoT > for FieldAttributes +where + IntoT : Into< AttributeSubformCollectionSetter >, +{ + #[ inline( always ) ] + fn assign( &mut self, component : IntoT ) + { + let component = component.into(); + self.subform_collection.option_assign( component ); + } +} + +impl< IntoT > Assign< AttributeSubformCollectionSetter, IntoT > for AttributeSubformCollectionSetter +where + IntoT : Into< AttributeSubformCollectionSetter >, +{ + #[ inline( always ) ] + fn assign( &mut self, component : IntoT ) + { + let component = component.into(); + self.name.assign( component.name ); + self.setter.assign( component.setter ); + self.debug.assign( component.debug ); + self.definition.assign( component.definition ); + } +} + +impl< IntoT > Assign< AttributePropertyName, IntoT > for AttributeSubformCollectionSetter +where + IntoT : Into< AttributePropertyName >, +{ + #[ inline( always ) ] + fn assign( &mut self, component : IntoT ) + { + self.name = component.into(); + } +} + +impl< IntoT > Assign< AttributePropertySetter, IntoT > for AttributeSubformCollectionSetter +where + IntoT : Into< AttributePropertySetter >, +{ + #[ inline( always ) ] + fn assign( &mut self, component : IntoT ) + { + self.setter = component.into(); + } +} + +impl< IntoT > Assign< AttributePropertyDefinition, IntoT > for AttributeSubformCollectionSetter +where + IntoT : Into< AttributePropertyDefinition >, +{ + #[ inline( always ) ] + fn assign( &mut self, component : IntoT ) + { + self.definition = component.into(); + } +} + +impl< IntoT > Assign< AttributePropertyDebug, IntoT > for AttributeSubformCollectionSetter +where + IntoT : Into< AttributePropertyDebug >, +{ + #[ inline( always ) ] + fn assign( &mut self, component : IntoT ) + { + self.debug = component.into(); + } +} + +impl syn::parse::Parse for AttributeSubformCollectionSetter { fn parse( input : syn::parse::ParseStream< '_ > ) -> syn::Result< Self > { - let mut name : Option< syn::Ident > = None; - let mut setter : Option< bool > = None; // Default is to generate a setter - let mut hint = false; - let mut definition : Option< syn::Type > = None; + let mut result = Self::default(); + + let error = | ident : &syn::Ident | -> syn::Error + { + let known = const_format::concatcp! + ( + "Known entries of attribute ", AttributeSubformCollectionSetter::KEYWORD, " are : ", + AttributePropertyName::KEYWORD, + ", ", AttributePropertySetter::KEYWORD, + ", ", AttributePropertyDebug::KEYWORD, + ", ", AttributePropertyDefinition::KEYWORD, + ".", + ); + syn_err! + ( + ident, + r#"Expects an attribute of format '#[ subform_collection( name = myName, setter = true, debug, definition = MyDefinition ) ]' + {known} + But got: '{}' +"#, + qt!{ #ident } + ) + }; while !input.is_empty() { @@ -304,36 +744,18 @@ impl syn::parse::Parse for AttributeContainerSetter if lookahead.peek( syn::Ident ) { let ident : syn::Ident = input.parse()?; - if ident == "name" + match ident.to_string().as_str() { - 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 ) ) ); + AttributePropertyName::KEYWORD => result.assign( AttributePropertyName::parse( input )? ), + AttributePropertySetter::KEYWORD => result.assign( AttributePropertySetter::parse( input )? ), + AttributePropertyDebug::KEYWORD => result.assign( AttributePropertyDebug::from( true ) ), + AttributePropertyDefinition::KEYWORD => result.assign( AttributePropertyDefinition::parse( input )? ), + _ => return Err( error( &ident ) ), } } else { - return Err( syn::Error::new( input.span(), "Expected 'name', 'setter', or 'definition' identifier. For example: `container( name = myName, setter = true, definition = MyDefinition )`" ) ); + return Err( lookahead.error() ); } // Optional comma handling @@ -343,7 +765,7 @@ impl syn::parse::Parse for AttributeContainerSetter } } - Ok( Self { name, setter, hint, definition } ) + Ok( result ) } } @@ -365,37 +787,139 @@ impl syn::parse::Parse for AttributeContainerSetter /// mame = field_name /// ``` -pub struct AttributeSubformSetter +#[ derive( Debug, Default ) ] +pub struct AttributeSubformEntrySetter { /// An optional identifier that names the setter. It is parsed from inputs /// like `name = my_field`. - pub name : Option< syn::Ident >, + pub name : AttributePropertyName, /// Disable generation of setter. - /// It still generate `_field_add` method, so it could be used to make a setter with custom arguments. - pub setter : Option< bool >, + /// It still generate `_field_subform_entry` method, so it could be used to make a setter with custom arguments. + pub setter : AttributePropertySetter, /// Specifies whether to provide a sketch of the subform setter as a hint. /// Defaults to `false`, which means no hint is provided unless explicitly requested. - pub hint : bool, + pub debug : AttributePropertyDebug, } -impl AttributeSubformSetter +impl AttributeSubformEntrySetter { /// Should setter be generated or not? pub fn setter( &self ) -> bool { - self.setter.is_none() || self.setter.unwrap() + self.setter.as_ref().is_none() || self.setter.as_ref().unwrap() } } -impl syn::parse::Parse for AttributeSubformSetter +impl AttributeComponent for AttributeSubformEntrySetter +{ + + const KEYWORD : &'static str = "subform_entry"; + + fn from_meta( attr : &syn::Attribute ) -> Result< Self > + { + match attr.meta + { + syn::Meta::List( ref meta_list ) => + { + syn::parse2::< AttributeSubformEntrySetter >( meta_list.tokens.clone() ) + }, + syn::Meta::Path( ref _path ) => + { + syn::parse2::< AttributeSubformEntrySetter >( Default::default() ) + }, + _ => return_syn_err!( attr, "Expects an attribute of format `#[ subform_entry ]` or `#[ subform_entry( name : child )` ], \nGot: {}", qt!{ #attr } ), + } + } + +} + +impl< IntoT > Assign< AttributeSubformEntrySetter, IntoT > for FieldAttributes +where + IntoT : Into< AttributeSubformEntrySetter >, +{ + #[ inline( always ) ] + fn assign( &mut self, component : IntoT ) + { + let component = component.into(); + self.subform_entry.option_assign( component ); + } +} + +impl< IntoT > Assign< AttributeSubformEntrySetter, IntoT > for AttributeSubformEntrySetter +where + IntoT : Into< AttributeSubformEntrySetter >, +{ + #[ inline( always ) ] + fn assign( &mut self, component : IntoT ) + { + let component = component.into(); + self.name.assign( component.name ); + self.setter.assign( component.setter ); + self.debug.assign( component.debug ); + } +} + +impl< IntoT > Assign< AttributePropertyName, IntoT > for AttributeSubformEntrySetter +where + IntoT : Into< AttributePropertyName >, +{ + #[ inline( always ) ] + fn assign( &mut self, component : IntoT ) + { + self.name = component.into(); + } +} + +impl< IntoT > Assign< AttributePropertySetter, IntoT > for AttributeSubformEntrySetter +where + IntoT : Into< AttributePropertySetter >, +{ + #[ inline( always ) ] + fn assign( &mut self, component : IntoT ) + { + self.setter = component.into(); + } +} + +impl< IntoT > Assign< AttributePropertyDebug, IntoT > for AttributeSubformEntrySetter +where + IntoT : Into< AttributePropertyDebug >, +{ + #[ inline( always ) ] + fn assign( &mut self, component : IntoT ) + { + self.debug = component.into(); + } +} + +impl syn::parse::Parse for AttributeSubformEntrySetter { fn parse( input : syn::parse::ParseStream< '_ > ) -> syn::Result< Self > { - let mut name : Option< syn::Ident > = None; - let mut setter : Option< bool > = None; - let mut hint = false; + let mut result = Self::default(); + + let error = | ident : &syn::Ident | -> syn::Error + { + let known = const_format::concatcp! + ( + "Known entries of attribute ", AttributeSubformEntrySetter::KEYWORD, " are : ", + AttributePropertyName::KEYWORD, + ", ", AttributePropertySetter::KEYWORD, + ", ", AttributePropertyDebug::KEYWORD, + ".", + ); + syn_err! + ( + ident, + r#"Expects an attribute of format '#[ subform( name = myName, setter = true ) ]' + {known} + But got: '{}' +"#, + qt!{ #ident } + ) + }; while !input.is_empty() { @@ -403,40 +927,107 @@ impl syn::parse::Parse for AttributeSubformSetter if lookahead.peek( syn::Ident ) { let ident : syn::Ident = input.parse()?; - if ident == "name" + match ident.to_string().as_str() { - 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 ) ) ); + AttributePropertyName::KEYWORD => result.assign( AttributePropertyName::parse( input )? ), + AttributePropertySetter::KEYWORD => result.assign( AttributePropertySetter::parse( input )? ), + AttributePropertyDebug::KEYWORD => result.assign( AttributePropertyDebug::from( true ) ), + _ => return Err( error( &ident ) ), } } else { - return Err( syn::Error::new( input.span(), "Expected 'name', 'setter', or 'definition' identifier. For example: `subform( name = myName, setter = true )`" ) ); + return Err( lookahead.error() ); } // Optional comma handling - if input.peek( syn::Token![,] ) + if input.peek( syn::Token![ , ] ) { - input.parse::< syn::Token![,] >()?; + input.parse::< syn::Token![ , ] >()?; } } - Ok( Self { name, setter, hint } ) + Ok( result ) } } + +// == attribute properties + +// = + +/// Marker type for attribute property to specify whether to provide a sketch as a hint. +/// Defaults to `false`, which means no hint is provided unless explicitly requested. +#[ derive( Debug, Default, Clone, Copy ) ] +pub struct DebugMarker; + +/// Specifies whether to provide a sketch as a hint. +/// Defaults to `false`, which means no hint is provided unless explicitly requested. +impl AttributePropertyComponent for DebugMarker +{ + const KEYWORD : &'static str = "debug"; +} + +/// Specifies whether to provide a sketch as a hint. +/// Defaults to `false`, which means no hint is provided unless explicitly requested. +pub type AttributePropertyDebug = AttributePropertyOptionalSingletone< DebugMarker >; + +// = + +/// Disable generation of setter. +/// Attributes still might generate some helper methods to reuse by custom setter. +#[ derive( Debug, Default, Clone, Copy ) ] +pub struct SetterMarker; + +impl AttributePropertyComponent for SetterMarker +{ + const KEYWORD : &'static str = "setter"; +} + +/// Disable generation of setter. +/// Attributes still might generate some helper methods to reuse by custom setter. +pub type AttributePropertySetter = AttributePropertyOptionalBoolean< SetterMarker >; + +// = + +/// Marker type for attribute property of optional identifier that names the setter. It is parsed from inputs +/// like `name = my_field`. +#[ derive( Debug, Default, Clone, Copy ) ] +pub struct NameMarker; + +impl AttributePropertyComponent for NameMarker +{ + const KEYWORD : &'static str = "name"; +} + +/// An optional identifier that names the setter. It is parsed from inputs +/// like `name = my_field`. +pub type AttributePropertyName = AttributePropertyOptionalSyn< syn::Ident, NameMarker >; + +// = + +/// Marker type for default value to use for a field. +#[ derive( Debug, Default, Clone, Copy ) ] +pub struct DefaultMarker; + +impl AttributePropertyComponent for DefaultMarker +{ + const KEYWORD : &'static str = "default"; +} + +/// An optional identifier that names the setter. It is parsed from inputs +/// like `name = my_field`. +pub type AttributePropertyDefault = AttributePropertyOptionalSyn< syn::Expr, DefaultMarker >; + +// = + +/// Marker type for definition of the collection former to use, e.g., `former::VectorFormer`. +#[ derive( Debug, Default, Clone, Copy ) ] +pub struct DefinitionMarker; + +impl AttributePropertyComponent for DefinitionMarker +{ + const KEYWORD : &'static str = "definition"; +} + +/// Definition of the collection former to use, e.g., `former::VectorFormer`. +pub type AttributePropertyDefinition = AttributePropertyOptionalSyn< syn::Type, DefinitionMarker >; diff --git a/module/core/former_meta/src/derive_former/struct_attrs.rs b/module/core/former_meta/src/derive_former/struct_attrs.rs index c2a15bb9cf..44e8273292 100644 --- a/module/core/former_meta/src/derive_former/struct_attrs.rs +++ b/module/core/former_meta/src/derive_former/struct_attrs.rs @@ -1,96 +1,90 @@ +//! +//! Attributes of the whole item. +//! use super::*; -use macro_tools::{ attr, Result }; -/// -/// Definition of a field. -/// +use macro_tools:: +{ + Result, + AttributeComponent, + AttributePropertyComponent, + AttributePropertyOptionalSingletone, +}; -/// -/// Attributes of a struct. -/// +use former_types::{ Assign, OptionExt }; -pub struct StructAttributes +/// Represents the attributes of a struct, including storage fields, mutator, and perform attributes. + +#[ derive( Debug, Default ) ] +pub struct ItemAttributes { - pub perform : Option< AttributePerform >, + /// Optional attribute for storage-specific fields. + /// This field is used to specify fields that should be part of the storage but not the final formed structure. pub storage_fields : Option< AttributeStorageFields >, + + /// Attribute for customizing the mutation process in a forming operation. + /// The `mutator` attribute allows for specifying whether a custom mutator should be used or if a sketch should be provided as a hint. pub mutator : AttributeMutator, + + /// Optional attribute for specifying a method to call after forming. + /// This attribute can hold information about a method that should be invoked after the form operation is complete. + pub perform : Option< AttributePerform >, } -impl StructAttributes +impl ItemAttributes { 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(); + let mut result = Self::default(); + + let error = | attr : &syn::Attribute | -> syn::Error + { + let known_attributes = const_format::concatcp! + ( + "Known attirbutes are : ", + "debug", + ", ", AttributeStorageFields::KEYWORD, + ", ", AttributeMutator::KEYWORD, + ", ", AttributePerform::KEYWORD, + ".", + ); + syn_err! + ( + attr, + "Expects an attribute of format '#[ attribute( key1 = val1, key2 = val2 ) ]'\n {known_attributes}\n But got: '{}'", + qt!{ #attr } + ) + }; for attr in attrs { - let key_ident = attr.path().get_ident() - .ok_or_else( || syn_err!( attr, "Expects an attribute of format #[ attribute( val ) ], but got:\n {}", qt!{ #attr } ) )?; + + let key_ident = attr.path().get_ident().ok_or_else( || error( attr ) )?; let key_str = format!( "{}", key_ident ); - if attr::is_standard( &key_str ) - { - continue; - } + // attributes does not have to be known + // 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 } ) ); - } + AttributeStorageFields::KEYWORD => result.assign( AttributeStorageFields::from_meta( attr )? ), + AttributeMutator::KEYWORD => result.assign( AttributeMutator::from_meta( attr )? ), + AttributePerform::KEYWORD => result.assign( AttributePerform::from_meta( attr )? ), + "debug" => {} + _ => {}, + // _ => return Err( error( attr ) ), + // attributes does not have to be known } } - Ok( StructAttributes { perform, storage_fields, mutator } ) + Ok( result ) } - /// /// Generate parts, used for generating `perform()`` method. /// @@ -157,6 +151,7 @@ impl StructAttributes || &*Box::leak( Box::new( syn::punctuated::Punctuated::new() ) ), | attr | &attr.fields ) + // qqq : find better solutioin // self.storage_fields @@ -171,40 +166,60 @@ impl StructAttributes } /// -/// Attribute to hold information about method to call after form. +/// Attribute to hold storage-specific fields. +/// Useful if formed structure should not have such fields. /// -/// `#[ perform( fn after1< 'a >() -> Option< &'a str > ) ]` +/// `#[ storage_fields( a : i32, b : Option< String > ) ]` /// -pub struct AttributePerform +#[ derive( Debug, Default ) ] +pub struct AttributeStorageFields { - pub signature : syn::Signature, + pub fields : syn::punctuated::Punctuated< syn::Field, syn::token::Comma >, } -impl syn::parse::Parse for AttributePerform +impl AttributeComponent for AttributeStorageFields { - fn parse( input : syn::parse::ParseStream< '_ > ) -> Result< Self > + + const KEYWORD : &'static str = "storage_fields"; + + fn from_meta( attr : &syn::Attribute ) -> Result< Self > { - // let input2; - Ok( Self + match attr.meta { - // paren_token : syn::parenthesized!( input2 in input ), - // signature : input2.parse()?, - signature : input.parse()?, - }) + syn::Meta::List( ref meta_list ) => + { + return 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 } ), + } } + } -/// -/// Attribute to hold storage-specific fields. -/// Useful if formed structure should not have such fields. -/// -/// `#[ storage_fields( a : i32, b : Option< String > ) ]` -/// +impl< IntoT > Assign< AttributeStorageFields, IntoT > for ItemAttributes +where + IntoT : Into< AttributeStorageFields >, +{ + #[ inline( always ) ] + fn assign( &mut self, component : IntoT ) + { + let component = component.into(); + self.storage_fields.option_assign( component ); + } +} -pub struct AttributeStorageFields +impl< IntoT > Assign< AttributeStorageFields, IntoT > for AttributeStorageFields +where + IntoT : Into< AttributeStorageFields >, { - pub fields : syn::punctuated::Punctuated< syn::Field, syn::token::Comma >, + #[ inline( always ) ] + fn assign( &mut self, component : IntoT ) + { + let component = component.into(); + self.fields = component.fields; + } } impl syn::parse::Parse for AttributeStorageFields @@ -212,17 +227,12 @@ impl syn::parse::Parse for AttributeStorageFields fn parse( input : syn::parse::ParseStream< '_ > ) -> syn::Result< Self > { - let fields : syn::punctuated::Punctuated< syn::Field, syn::Token![,] > = - input.parse_terminated( syn::Field::parse_named, Token![,] )?; + let fields : syn::punctuated::Punctuated< syn::Field, syn::Token![ , ] > = + input.parse_terminated( syn::Field::parse_named, Token![ , ] )?; Ok( Self { fields, - // fields : syn::Fields::Named( syn::FieldsNamed - // { - // brace_token : Default::default(), - // named : fields, - // }), }) } } @@ -235,7 +245,7 @@ impl syn::parse::Parse for AttributeStorageFields /// /// ## Example of code /// ```ignore -/// custom = true, hint = true +/// custom, debug /// ``` #[ derive( Debug, Default ) ] @@ -243,18 +253,106 @@ pub struct AttributeMutator { /// Indicates whether a custom mutator should be generated. /// Defaults to `false`, meaning no custom mutator is generated unless explicitly requested. - pub custom : bool, + pub custom : AttributePropertyCustom, /// Specifies whether to provide a sketch of the mutator as a hint. /// Defaults to `false`, which means no hint is provided unless explicitly requested. - pub hint : bool, + pub debug : AttributePropertyDebug, +} + +impl AttributeComponent for AttributeMutator +{ + const KEYWORD : &'static str = "mutator"; + + fn from_meta( attr : &syn::Attribute ) -> Result< Self > + { + match attr.meta + { + syn::Meta::List( ref meta_list ) => + { + return syn::parse2::< AttributeMutator >( meta_list.tokens.clone() ); + }, + syn::Meta::Path( ref _path ) => + { + return Ok( Default::default() ) + }, + _ => return_syn_err!( attr, "Expects an attribute of format `#[ mutator( custom ) ]`. \nGot: {}", qt!{ #attr } ), + } + } + +} + +impl< IntoT > Assign< AttributeMutator, IntoT > for ItemAttributes +where + IntoT : Into< AttributeMutator >, +{ + #[ inline( always ) ] + fn assign( &mut self, component : IntoT ) + { + let component = component.into(); + self.mutator.assign( component ); + } +} + +impl< IntoT > Assign< AttributeMutator, IntoT > for AttributeMutator +where + IntoT : Into< AttributeMutator >, +{ + #[ inline( always ) ] + fn assign( &mut self, component : IntoT ) + { + let component = component.into(); + self.custom.assign( component.custom ); + self.debug.assign( component.debug ); + } +} + +impl< IntoT > Assign< AttributePropertyDebug, IntoT > for AttributeMutator +where + IntoT : Into< AttributePropertyDebug >, +{ + #[ inline( always ) ] + fn assign( &mut self, component : IntoT ) + { + self.debug = component.into(); + } +} + +impl< IntoT > Assign< AttributePropertyCustom, IntoT > for AttributeMutator +where + IntoT : Into< AttributePropertyCustom >, +{ + #[ inline( always ) ] + fn assign( &mut self, component : IntoT ) + { + self.custom = component.into(); + } } impl syn::parse::Parse for AttributeMutator { fn parse( input : syn::parse::ParseStream< '_ > ) -> syn::Result< Self > { - let mut custom = false; - let mut hint = false; + let mut result = Self::default(); + + let error = | ident : &syn::Ident | -> syn::Error + { + let known = const_format::concatcp! + ( + "Known entries of attribute ", AttributeMutator::KEYWORD, " are : ", + AttributePropertyCustom::KEYWORD, + ", ", AttributePropertyDebug::KEYWORD, + ".", + ); + syn_err! + ( + ident, + r#"Expects an attribute of format '#[ mutator( custom ) ]' + {known} + But got: '{}' +"#, + qt!{ #ident } + ) + }; while !input.is_empty() { @@ -262,20 +360,11 @@ impl syn::parse::Parse for AttributeMutator 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 + match ident.to_string().as_str() { - return Err( syn::Error::new_spanned( &ident, format!( "Unexpected identifier '{}'. Expected 'custom' or 'hint'.", ident ) ) ); + AttributePropertyCustom::KEYWORD => result.assign( AttributePropertyCustom::from( true ) ), + AttributePropertyDebug::KEYWORD => result.assign( AttributePropertyDebug::from( true ) ), + _ => return Err( error( &ident ) ), } } else @@ -284,16 +373,111 @@ impl syn::parse::Parse for AttributeMutator } // Optional comma handling - if input.peek( syn::Token![,] ) + if input.peek( syn::Token![ , ] ) { - input.parse::< syn::Token![,] >()?; + input.parse::< syn::Token![ , ] >()?; } } + Ok( result ) + } +} + +/// +/// Attribute to hold information about method to call after form. +/// +/// `#[ perform( fn after1< 'a >() -> Option< &'a str > ) ]` +/// + +#[ derive( Debug ) ] +pub struct AttributePerform +{ + pub signature : syn::Signature, +} + +impl AttributeComponent for AttributePerform +{ + const KEYWORD : &'static str = "perform"; + + fn from_meta( attr : &syn::Attribute ) -> Result< Self > + { + + match attr.meta + { + syn::Meta::List( ref meta_list ) => + { + return 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 } ), + } + } + +} + +impl syn::parse::Parse for AttributePerform +{ + fn parse( input : syn::parse::ParseStream< '_ > ) -> Result< Self > + { Ok( Self { - custom, - hint, + signature : input.parse()?, }) } } + +impl< IntoT > Assign< AttributePerform, IntoT > for ItemAttributes +where + IntoT : Into< AttributePerform >, +{ + #[ inline( always ) ] + fn assign( &mut self, component : IntoT ) + { + let component = component.into(); + self.perform.option_assign( component ); + } +} + +impl< IntoT > Assign< AttributePerform, IntoT > for AttributePerform +where + IntoT : Into< AttributePerform >, +{ + #[ inline( always ) ] + fn assign( &mut self, component : IntoT ) + { + let component = component.into(); + self.signature = component.signature; + } +} + +// == attribute properties + +/// Marker type for attribute property to specify whether to provide a sketch as a hint. +/// Defaults to `false`, which means no hint is provided unless explicitly requested. +#[ derive( Debug, Default, Clone, Copy ) ] +pub struct DebugMarker; + +impl AttributePropertyComponent for DebugMarker +{ + const KEYWORD : &'static str = "debug"; +} + +/// Specifies whether to provide a sketch as a hint. +/// Defaults to `false`, which means no hint is provided unless explicitly requested. +pub type AttributePropertyDebug = AttributePropertyOptionalSingletone< DebugMarker >; + +// = + +/// Marker type for attribute property to indicates whether a custom code should be generated. +/// Defaults to `false`, meaning no custom code is generated unless explicitly requested. +#[ derive( Debug, Default, Clone, Copy ) ] +pub struct CustomMarker; + +impl AttributePropertyComponent for CustomMarker +{ + const KEYWORD : &'static str = "custom"; +} + +/// Indicates whether a custom code should be generated. +/// Defaults to `false`, meaning no custom code is generated unless explicitly requested. +pub type AttributePropertyCustom = AttributePropertyOptionalSingletone< CustomMarker >; diff --git a/module/core/former_meta/src/lib.rs b/module/core/former_meta/src/lib.rs index cf131eef6f..3a4d6af4d0 100644 --- a/module/core/former_meta/src/lib.rs +++ b/module/core/former_meta/src/lib.rs @@ -3,8 +3,14 @@ #![ doc( html_root_url = "https://docs.rs/former_derive_meta/latest/former_derive_meta/" ) ] #![ doc = include_str!( concat!( env!( "CARGO_MANIFEST_DIR" ), "/", "Readme.md" ) ) ] +#[ allow( unused_imports ) ] +use macro_tools::prelude::*; + +#[ cfg( feature = "derive_former" ) ] +mod derive_former; + #[ cfg( feature = "enabled" ) ] -// #[ cfg( feature = "derive_component_from" ) ] +#[ cfg( feature = "derive_components" ) ] mod component { @@ -26,11 +32,6 @@ mod component } -#[ allow( unused_imports ) ] -use macro_tools::prelude::*; -#[ cfg( feature = "derive_former" ) ] -mod derive_former; - /// Derive macro for generating a `Former` struct, applying a Builder Pattern to the annotated struct. /// /// This macro simplifies the construction of complex objects by automatically generating a builder (former) for @@ -48,7 +49,7 @@ mod derive_former; /// /// - `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. +/// - `collection`: Marks the field as a collection 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. /// /// # Usage Example @@ -102,7 +103,7 @@ mod derive_former; attributes ( debug, perform, storage_fields, mutator, // struct attributes - former, scalar, container, subform, // field attributes + former, scalar, subform_scalar, subform_collection, subform_entry, // field attributes ) ) ] @@ -169,10 +170,10 @@ pub fn component_from( input : proc_macro::TokenStream ) -> proc_macro::TokenStr } } -/// Derives the `ComponentAssign` trait for struct fields, allowing each field to be set +/// Derives the `Assign` trait for struct fields, allowing each field to be set /// with a value that can be converted into the field's type. /// -/// This macro facilitates the automatic implementation of the `ComponentAssign` trait for all +/// This macro facilitates the automatic implementation of the `Assign` trait for all /// fields within a struct, leveraging the power of Rust's type system to ensure type safety /// and conversion logic. It is particularly useful for builder patterns or mutating instances /// of data structures in a fluent and ergonomic manner. @@ -187,12 +188,12 @@ pub fn component_from( input : proc_macro::TokenStream ) -> proc_macro::TokenStr /// /// # Input Code Example /// -/// Given a struct definition annotated with `#[ derive( ComponentAssign ) ]` : +/// Given a struct definition annotated with `#[ derive( Assign ) ]` : /// /// ```rust -/// use former::ComponentAssign; +/// use former::Assign; /// -/// #[ derive( Default, PartialEq, Debug, former::ComponentAssign ) ] +/// #[ derive( Default, PartialEq, Debug, former::Assign ) ] /// struct Person /// { /// age : i32, @@ -210,7 +211,7 @@ pub fn component_from( input : proc_macro::TokenStream ) -> proc_macro::TokenStr /// The procedural macro generates the following implementations for `Person` : /// /// ```rust -/// use former::ComponentAssign; +/// use former::Assign; /// /// #[ derive( Default, PartialEq, Debug ) ] /// struct Person @@ -219,7 +220,7 @@ pub fn component_from( input : proc_macro::TokenStream ) -> proc_macro::TokenStr /// name : String, /// } /// -/// impl< IntoT > ComponentAssign< i32, IntoT > for Person +/// impl< IntoT > Assign< i32, IntoT > for Person /// where /// IntoT : Into< i32 >, /// { @@ -229,7 +230,7 @@ pub fn component_from( input : proc_macro::TokenStream ) -> proc_macro::TokenStr /// } /// } /// -/// impl< IntoT > ComponentAssign< String, IntoT > for Person +/// impl< IntoT > Assign< String, IntoT > for Person /// where /// IntoT : Into< String >, /// { @@ -249,7 +250,7 @@ pub fn component_from( input : proc_macro::TokenStream ) -> proc_macro::TokenStr #[ cfg( feature = "enabled" ) ] #[ cfg( feature = "derive_component_assign" ) ] -#[ proc_macro_derive( ComponentAssign, attributes( debug ) ) ] +#[ proc_macro_derive( Assign, attributes( debug ) ) ] pub fn component_assign( input : proc_macro::TokenStream ) -> proc_macro::TokenStream { let result = component::component_assign::component_assign( input ); @@ -273,7 +274,7 @@ pub fn component_assign( input : proc_macro::TokenStream ) -> proc_macro::TokenS /// # Conditions /// /// - This macro is only enabled when the `derive_components_assign` feature is active in your `Cargo.toml`. -/// - The type must implement `ComponentAssign` (`derive( ComponentAssign )`) +/// - The type must implement `Assign` (`derive( Assign )`) /// /// # Limitations /// This trait cannot be derived, if the struct has fields with identical types @@ -283,9 +284,9 @@ pub fn component_assign( input : proc_macro::TokenStream ) -> proc_macro::TokenS /// An example when we encapsulate parameters passed to a function in a struct. /// /// ```rust -/// use former::{ ComponentAssign, ComponentsAssign }; +/// use former::{ Assign, ComponentsAssign }; /// -/// #[ derive( Default, ComponentAssign, ComponentsAssign ) ] +/// #[ derive( Default, Assign, ComponentsAssign ) ] /// struct BigOpts /// { /// cond : bool, @@ -293,7 +294,7 @@ pub fn component_assign( input : proc_macro::TokenStream ) -> proc_macro::TokenS /// str : String, /// } /// -/// #[ derive( Default, ComponentAssign, ComponentsAssign ) ] +/// #[ derive( Default, Assign, ComponentsAssign ) ] /// struct SmallerOpts /// { /// cond: bool, @@ -342,7 +343,7 @@ pub fn component_assign( input : proc_macro::TokenStream ) -> proc_macro::TokenS /// Which expands approximately into : /// /// ```rust -/// use former::{ ComponentAssign, ComponentsAssign }; +/// use former::{ Assign, ComponentsAssign }; /// /// #[derive(Default)] /// struct BigOpts @@ -352,7 +353,7 @@ pub fn component_assign( input : proc_macro::TokenStream ) -> proc_macro::TokenS /// str : String, /// } /// -/// impl< IntoT > ComponentAssign< bool, IntoT > for BigOpts +/// impl< IntoT > Assign< bool, IntoT > for BigOpts /// where /// IntoT : Into< bool >, /// { @@ -362,7 +363,7 @@ pub fn component_assign( input : proc_macro::TokenStream ) -> proc_macro::TokenS /// } /// } /// -/// impl< IntoT > ComponentAssign< i32, IntoT > for BigOpts +/// impl< IntoT > Assign< i32, IntoT > for BigOpts /// where /// IntoT : Into< i32 >, /// { @@ -372,7 +373,7 @@ pub fn component_assign( input : proc_macro::TokenStream ) -> proc_macro::TokenS /// } /// } /// -/// impl< IntoT > ComponentAssign< String, IntoT > for BigOpts +/// impl< IntoT > Assign< String, IntoT > for BigOpts /// where /// IntoT : Into< String >, /// { @@ -394,9 +395,9 @@ pub fn component_assign( input : proc_macro::TokenStream ) -> proc_macro::TokenS /// /// impl< T, IntoT > BigOptsComponentsAssign< IntoT > for T /// where -/// T : former::ComponentAssign< bool, IntoT >, -/// T : former::ComponentAssign< i32, IntoT >, -/// T : former::ComponentAssign< String, IntoT >, +/// T : former::Assign< bool, IntoT >, +/// T : former::Assign< i32, IntoT >, +/// T : former::Assign< String, IntoT >, /// IntoT : Into< bool >, /// IntoT : Into< i32 >, /// IntoT : Into< String >, @@ -404,9 +405,9 @@ pub fn component_assign( input : proc_macro::TokenStream ) -> proc_macro::TokenS /// { /// fn components_assign( &mut self, component : IntoT ) /// { -/// former::ComponentAssign::< bool, _ >::assign( self, component.clone() ); -/// former::ComponentAssign::< i32, _ >::assign( self, component.clone() ); -/// former::ComponentAssign::< String, _ >::assign( self, component.clone() ); +/// former::Assign::< bool, _ >::assign( self, component.clone() ); +/// former::Assign::< i32, _ >::assign( self, component.clone() ); +/// former::Assign::< String, _ >::assign( self, component.clone() ); /// } /// } /// @@ -417,7 +418,7 @@ pub fn component_assign( input : proc_macro::TokenStream ) -> proc_macro::TokenS /// int : i32, /// } /// -/// impl< IntoT > ComponentAssign< bool, IntoT > for SmallerOpts +/// impl< IntoT > Assign< bool, IntoT > for SmallerOpts /// where /// IntoT : Into< bool >, /// { @@ -427,7 +428,7 @@ pub fn component_assign( input : proc_macro::TokenStream ) -> proc_macro::TokenS /// } /// } /// -/// impl< IntoT > ComponentAssign< i32, IntoT > for SmallerOpts +/// impl< IntoT > Assign< i32, IntoT > for SmallerOpts /// where /// IntoT : Into< i32 >, /// { @@ -448,16 +449,16 @@ pub fn component_assign( input : proc_macro::TokenStream ) -> proc_macro::TokenS /// /// impl< T, IntoT > SmallerOptsComponentsAssign< IntoT > for T /// where -/// T : former::ComponentAssign< bool, IntoT >, -/// T : former::ComponentAssign< i32, IntoT >, +/// T : former::Assign< bool, IntoT >, +/// T : former::Assign< i32, IntoT >, /// IntoT : Into< bool >, /// IntoT : Into< i32 >, /// IntoT : Clone, /// { /// fn smaller_opts_assign( &mut self, component : IntoT ) /// { -/// former::ComponentAssign::< bool, _ >::assign( self, component.clone() ); -/// former::ComponentAssign::< i32, _ >::assign( self, component.clone() ); +/// former::Assign::< bool, _ >::assign( self, component.clone() ); +/// former::Assign::< i32, _ >::assign( self, component.clone() ); /// } /// } /// @@ -613,4 +614,3 @@ pub fn from_components( input : proc_macro::TokenStream ) -> proc_macro::TokenSt Err( err ) => err.to_compile_error().into(), } } - diff --git a/module/core/former_types/Cargo.toml b/module/core/former_types/Cargo.toml new file mode 100644 index 0000000000..d6b6ca5fb3 --- /dev/null +++ b/module/core/former_types/Cargo.toml @@ -0,0 +1,56 @@ +[package] +name = "former_types" +version = "2.2.0" +edition = "2021" +authors = [ + "Kostiantyn Wandalen ", +] +license = "MIT" +readme = "Readme.md" +documentation = "https://docs.rs/former" +repository = "https://github.com/Wandalen/wTools/tree/master/module/core/former" +homepage = "https://github.com/Wandalen/wTools/tree/master/module/core/former" +description = """ +A flexible and extensible implementation of the builder pattern. Its compile-time structures and traits that are not generated but reused. +""" +categories = [ "algorithms", "development-tools" ] +keywords = [ "fundamental", "general-purpose", "builder-pattern" ] + +[lints] +workspace = true + +[package.metadata.docs.rs] +features = [ "full" ] +all-features = false + +[features] + +no_std = [ "collection_tools/no_std" ] +use_alloc = [ "no_std", "collection_tools/use_alloc" ] + +default = [ + "enabled", + "derive_former", + "types_components", + "types_component_assign", +] +full = [ + "enabled", + "derive_former", + "types_components", + "types_component_assign", +] +enabled = [ "collection_tools/enabled" ] + +derive_former = [] +types_components = [] +types_component_assign = [ "types_components" ] + + +[dependencies] +collection_tools = { workspace = true, features = [ "collection_constructors" ] } +# qqq : optimize also make sure collection_tools expose enough features + + +[dev-dependencies] +test_tools = { workspace = true, features = [ "full" ] } diff --git a/module/core/former_types/License b/module/core/former_types/License new file mode 100644 index 0000000000..e3e9e057cf --- /dev/null +++ b/module/core/former_types/License @@ -0,0 +1,23 @@ +Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/module/core/former_types/Readme.md b/module/core/former_types/Readme.md new file mode 100644 index 0000000000..42346b3bc0 --- /dev/null +++ b/module/core/former_types/Readme.md @@ -0,0 +1,70 @@ + + +# Module :: former_types + + + [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/module_former_types_push.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/module_former_types_push.yml) [![docs.rs](https://img.shields.io/docsrs/former_types?color=e3e8f0&logo=docs.rs)](https://docs.rs/former_types) [![Open in Gitpod](https://raster.shields.io/static/v1?label=try&message=online&color=eee&logo=gitpod&logoColor=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fcore%2Fformer_types%2Fexamples%2Fformer_types_trivial.rs,RUN_POSTFIX=--example%20former_types_trivial/https://github.com/Wandalen/wTools) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) + + +A flexible and extensible implementation of the builder pattern. Its compile-time structures and traits that are not generated but reused. + +## Example: Using Trait Assign + +Demonstrates setting various components (fields) of a struct. + +The `former_types` crate provides a generic interface for setting components on an object. This example defines a `Person` struct +and implements the `Assign` trait for its fields. It shows how to use these implementations to set the fields of a `Person` +instance using different types that can be converted into the required types. + +```rust +#[ cfg( any( not( feature = "derive_former" ), not( feature = "enabled" ) ) ) ] +fn main() {} + +#[ cfg( all( feature = "derive_former", feature = "enabled" ) ) ] +fn main() +{ + use former_types::Assign; + + #[ derive( Default, PartialEq, Debug ) ] + struct Person + { + age : i32, + name : String, + } + + impl< IntoT > Assign< i32, IntoT > for Person + where + IntoT : Into< i32 >, + { + fn assign( &mut self, component : IntoT ) + { + self.age = component.into(); + } + } + + impl< IntoT > Assign< String, IntoT > for Person + where + IntoT : Into< String >, + { + fn assign( &mut self, component : IntoT ) + { + self.name = component.into(); + } + } + + let mut got : Person = Default::default(); + got.assign( 13 ); + got.assign( "John" ); + assert_eq!( got, Person { age : 13, name : "John".to_string() } ); + dbg!( got ); + // > Person { + // > age: 13, + // > name: "John", + // > } + +} +``` + +Try out `cargo run --example former_types_trivial`. +
+[See code](./examples/former_types_trivial.rs). diff --git a/module/core/former_types/examples/former_types_trivial.rs b/module/core/former_types/examples/former_types_trivial.rs new file mode 100644 index 0000000000..c379293640 --- /dev/null +++ b/module/core/former_types/examples/former_types_trivial.rs @@ -0,0 +1,68 @@ +//! +//! ## Example: Using Trait Assign +//! +//! Demonstrates setting various components (fields) of a struct. +//! +//! The `former_types` crate provides a generic interface for setting components on an object. This example defines a `Person` struct +//! and implements the `Assign` trait for its fields. It shows how to use these implementations to set the fields of a `Person` +//! instance using different types that can be converted into the required types. +//! +//! ## Explanation +//! +//! - **Person Struct**: The `Person` struct has two fields: `age` (an integer) and `name` (a string). The `Default` and `PartialEq` traits are derived to facilitate default construction and comparison. +//! +//! - **Assign Implementations**: The `Assign` trait is implemented for the `age` and `name` fields of the `Person` struct. +//! - For `age`: The trait is implemented for any type that can be converted into an `i32`. +//! - For `name`: The trait is implemented for any type that can be converted into a `String`. +//! +//! - **Usage**: An instance of `Person` is created using the default constructor, and then the `assign` method is used to set the `age` and `name` fields. +//! - `got.assign( 13 )`: Assigns the integer `13` to the `age` field. +//! - `got.assign( "John" )`: Assigns the string `"John"` to the `name` field. +//! + +#[ cfg( any( not( feature = "derive_former" ), not( feature = "enabled" ) ) ) ] +fn main() {} + +#[ cfg( all( feature = "derive_former", feature = "enabled" ) ) ] +fn main() +{ + use former_types::Assign; + + #[ derive( Default, PartialEq, Debug ) ] + struct Person + { + age : i32, + name : String, + } + + impl< IntoT > Assign< i32, IntoT > for Person + where + IntoT : Into< i32 >, + { + fn assign( &mut self, component : IntoT ) + { + self.age = component.into(); + } + } + + impl< IntoT > Assign< String, IntoT > for Person + where + IntoT : Into< String >, + { + fn assign( &mut self, component : IntoT ) + { + self.name = component.into(); + } + } + + let mut got : Person = Default::default(); + got.assign( 13 ); + got.assign( "John" ); + assert_eq!( got, Person { age : 13, name : "John".to_string() } ); + dbg!( got ); + // > Person { + // > age: 13, + // > name: "John", + // > } + +} diff --git a/module/core/former/src/axiomatic.rs b/module/core/former_types/src/axiomatic.rs similarity index 100% rename from module/core/former/src/axiomatic.rs rename to module/core/former_types/src/axiomatic.rs diff --git a/module/core/former/src/container.rs b/module/core/former_types/src/collection.rs similarity index 62% rename from module/core/former/src/container.rs rename to module/core/former_types/src/collection.rs index 2e0897da73..eac724c018 100644 --- a/module/core/former/src/container.rs +++ b/module/core/former_types/src/collection.rs @@ -1,8 +1,8 @@ //! //! 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. +//! of collection data structures within a builder pattern context. It provides a comprehensive +//! interface for adding, managing, and converting elements within various types of collections, +//! such as vectors, hash maps, and custom collection implementations. //! /// Internal namespace. @@ -11,18 +11,18 @@ pub( crate ) mod private use crate::*; - /// Facilitates the conversion of container entries to their corresponding value representations. + /// Facilitates the conversion of collection entries to their corresponding value representations. /// - /// 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 + /// This trait is utilized to transform an entry of a collection into a value, abstracting the operation of collections + /// like vectors or hash maps. It ensures that even in complex collection structures, entries can be seamlessly managed /// and manipulated as values. - pub trait EntryToVal< Container > + pub trait EntryToVal< Collection > { - /// The type of values stored in the container. This might be distinct from `Entry` in complex containers. + /// The type of values stored in the collection. This might be distinct from `Entry` in complex collections. /// 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 + /// Converts an entry into a value representation specific to the type of collection. 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; @@ -30,7 +30,7 @@ pub( crate ) mod private impl< C, E > EntryToVal< C > for E where - C : Container< Entry = E >, + C : Collection< Entry = E >, { type Val = C::Val; @@ -40,23 +40,23 @@ pub( crate ) mod private } } - /// Provides a mechanism for transforming a value back into a container-specific entry format. + /// Provides a mechanism for transforming a value back into a collection-specific entry format. /// - /// This trait is particularly valuable in scenarios where the operations on a container require + /// This trait is particularly valuable in scenarios where the operations on a collection 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 > + pub trait CollectionValToEntry< Val > { - /// The specific type of entry that corresponds to the value within the container. + /// The specific type of entry that corresponds to the value within the collection. /// 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. + /// Converts a value into a collection-specific entry, facilitating operations that modify + /// the collection. This method is key for ensuring that values can be correctly integrated + /// back into the collection, particularly when the entry type is more complex than the value. /// /// # Parameters /// * `val` - The value to be converted into an entry. @@ -66,11 +66,11 @@ pub( crate ) mod private /// /// # Example /// ``` - /// use former::ContainerValToEntry; + /// use former_types::CollectionValToEntry; // use crate `former` instead of crate `former_types` unless you need to use crate `former_types` directly /// /// struct PairMap; /// - /// impl ContainerValToEntry< ( i32, i32 ) > for PairMap + /// impl CollectionValToEntry< ( i32, i32 ) > for PairMap /// { /// type Entry = ( String, i32 ); /// @@ -83,28 +83,28 @@ pub( crate ) mod private fn val_to_entry( val : Val ) -> Self::Entry; } - /// Facilitates the conversion of values back into entries for specific container types. + /// Facilitates the conversion of values back into entries for specific collection types. /// - /// This trait wraps the functionality of `ContainerValToEntry`, providing a more ergonomic + /// This trait wraps the functionality of `CollectionValToEntry`, 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 + /// in maintaining the integrity of collection 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 > + /// and other associative collections. + pub trait ValToEntry< Collection > { - /// Represents the type of entry that corresponds to the value within the container. + /// Represents the type of entry that corresponds to the value within the collection. 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, + /// Transforms the instance (value) into an entry compatible with the specified collection. + /// This conversion is essential for operations like insertion or modification within the collection, /// 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. + /// Returns the entry constructed from the instance of the value, ready for integration into the collection. /// /// # Example /// ``` - /// use former::ValToEntry; + /// use former_types::ValToEntry; // use crate `former` instead of crate `former_types` unless you need to use crate `former_types` directly /// /// struct PairMap; /// @@ -123,60 +123,60 @@ pub( crate ) mod private impl< C, Val > ValToEntry< C > for Val where - C : ContainerValToEntry< Val >, + C : CollectionValToEntry< Val >, { type Entry = C::Entry; - /// Invokes the `val_to_entry` function of the `ContainerValToEntry` trait to convert the value to an entry. + /// Invokes the `val_to_entry` function of the `CollectionValToEntry` 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. + /// Represents a collection by defining the types of entries and values it handles. /// - /// 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 + /// This trait abstracts the nature of collections in data structures, facilitating the handling of contained + /// entries and values, especially in scenarios where the structure of the collection allows for complex relationships, + /// such as `HashMap`s. It not only identifies what constitutes an entry and a value in the context of the collection /// but also provides utility for converting between these two, which is critical in operations involving entry manipulation /// and value retrieval. - pub trait Container + pub trait Collection { - /// The type of entries that can be added to the container. This type can differ from `Val` in containers like `HashMap`, + /// The type of entries that can be added to the collection. This type can differ from `Val` in collections 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. + /// The type of values stored in the collection. This might be distinct from `Entry` in complex collections. /// 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. + /// Converts an entry to its corresponding value within the collection. This function is essential for abstracting + /// the collection'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. + /// Provides functionality to add individual entries to a collection. /// - /// 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 + /// This trait extends the basic `Collection` trait by introducing a method to add entries to a collection. + /// It is designed to handle the collection's specific requirements and rules for adding entries, such as /// managing duplicates, maintaining order, or handling capacity constraints. - pub trait ContainerAdd : Container + pub trait CollectionAdd : Collection { - /// Adds an entry to the container and returns a boolean indicating the success of the operation. + /// Adds an entry to the collection 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, + /// Implementations should ensure that the entry is added according to the rules of the collection, /// 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. + /// * `e`: The entry to be added to the collection, where the type `Entry` is defined by the `Collection` 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. + /// the entry already existing in the collection or the collection reaching its capacity. /// /// # Examples /// @@ -184,14 +184,14 @@ pub( crate ) mod private /// /// ```rust /// - /// use former::{ Container, ContainerAdd }; + /// use former_types::{ Collection, CollectionAdd }; // use crate `former` instead of crate `former_types` unless you need to use crate `former_types` directly /// - /// struct MyContainer + /// struct MyCollection /// { /// entries : Vec< i32 >, /// } /// - /// impl Container for MyContainer + /// impl Collection for MyCollection /// { /// type Entry = i32; /// type Val = i32; @@ -204,7 +204,7 @@ pub( crate ) mod private /// /// } /// - /// impl ContainerAdd for MyContainer + /// impl CollectionAdd for MyCollection /// { /// fn add( &mut self, e : Self::Entry ) -> bool /// { @@ -220,49 +220,49 @@ pub( crate ) mod private /// } /// } /// - /// let mut container = MyContainer { entries : vec![] }; - /// assert!( container.add( 10 ) ); // Returns true, entry added - /// assert!( !container.add( 10 ) ); // Returns false, entry already exists + /// let mut collection = MyCollection { entries : vec![] }; + /// assert!( collection.add( 10 ) ); // Returns true, entry added + /// assert!( !collection.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. + /// Defines the capability to replace all entries in a collection with a new set of entries. /// - /// 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 + /// This trait extends the `Collection` trait by providing a method to replace the existing entries in + /// the collection with a new set. This can be useful for resetting the collection's contents or bulk-updating /// them based on external criteria or operations. - pub trait ContainerAssign : Container + pub trait CollectionAssign : Collection where Self : IntoIterator< Item = Self::Entry >, { - /// Replaces all entries in the container with the provided entries and returns the count of new entries added. + /// Replaces all entries in the collection 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. + /// This method clears the existing entries and populates the collection with new ones provided by an iterator. + /// It is ideal for scenarios where the collection 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. + /// * `entries` : An iterator over the entries to be added to the collection. The entries must conform to + /// the `Entry` type defined by the `Collection` 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 + /// Returns the number of entries successfully added to the collection. This count may differ from the total + /// number of entries in the iterator if the collection imposes restrictions such as capacity limits or duplicate /// handling. /// /// # Examples /// /// ```rust - /// use former::{ Container, ContainerAssign }; + /// use former_types::{ Collection, CollectionAssign }; // use crate `former` instead of crate `former_types` unless you need to use crate `former_types` directly /// - /// struct MyContainer + /// struct MyCollection /// { /// entries : Vec< i32 >, /// } /// - /// impl Container for MyContainer + /// impl Collection for MyCollection /// { /// type Entry = i32; /// type Val = i32; @@ -275,12 +275,12 @@ pub( crate ) mod private /// /// } /// - /// impl IntoIterator for MyContainer + /// impl IntoIterator for MyCollection /// { /// type Item = i32; - /// type IntoIter = std::vec::IntoIter< i32 >; - /// // type IntoIter = collection_tools::vec::IntoIter< i32 >; - /// // qqq : zzz : make sure collection_tools has itearators + /// // type IntoIter = std::vec::IntoIter< i32 >; + /// type IntoIter = collection_tools::vec::IntoIter< i32 >; + /// // qqq : zzz : make sure collection_tools has itearators -- done /// /// fn into_iter( self ) -> Self::IntoIter /// { @@ -288,7 +288,7 @@ pub( crate ) mod private /// } /// } /// - /// impl ContainerAssign for MyContainer + /// impl CollectionAssign for MyCollection /// { /// fn assign< Entries >( &mut self, entries : Entries ) -> usize /// where @@ -300,9 +300,9 @@ pub( crate ) mod private /// } /// } /// - /// let mut container = MyContainer { entries : vec![ 1, 2, 3 ] }; + /// let mut collection = MyCollection { 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 ] + /// assert_eq!( collection.assign( new_elements ), 3 ); // Collection now contains [ 4, 5, 6 ] /// ``` fn assign< Entries >( &mut self, entries : Entries ) -> usize where @@ -311,12 +311,12 @@ pub( crate ) mod private // = - /// A builder structure for constructing containers with a fluent and flexible interface. + /// A builder structure for constructing collections with a fluent and flexible interface. #[ derive( Default ) ] - pub struct ContainerFormer< E, Definition > + pub struct CollectionFormer< E, Definition > where Definition : FormerDefinition, - Definition::Storage : ContainerAdd< Entry = E >, + Definition::Storage : CollectionAdd< Entry = E >, { storage : Definition::Storage, context : core::option::Option< Definition::Context >, @@ -324,15 +324,15 @@ pub( crate ) mod private } use core::fmt; - impl< E, Definition > fmt::Debug for ContainerFormer< E, Definition > + impl< E, Definition > fmt::Debug for CollectionFormer< E, Definition > where Definition : FormerDefinition, - Definition::Storage : ContainerAdd< Entry = E >, + Definition::Storage : CollectionAdd< Entry = E >, { fn fmt( &self, f : &mut fmt::Formatter< '_ > ) -> fmt::Result { f - .debug_struct( "ContainerFormer" ) + .debug_struct( "CollectionFormer" ) .field( "storage", &"Storage Present" ) .field( "context", &self.context.as_ref().map( |_| "Context Present" ) ) .field( "on_end", &self.on_end.as_ref().map( |_| "End Present" ) ) @@ -340,13 +340,13 @@ pub( crate ) mod private } } - impl< E, Definition > ContainerFormer< E, Definition > + impl< E, Definition > CollectionFormer< E, Definition > where Definition : FormerDefinition, - Definition::Storage : ContainerAdd< Entry = E >, + Definition::Storage : CollectionAdd< 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. + /// Begins the construction process of a collection with optional initial storage and context, + /// setting up an `on_end` completion handler to finalize the collection's construction. #[ inline( always ) ] pub fn begin ( @@ -419,14 +419,14 @@ pub( crate ) mod private } } - impl< E, Storage, Formed, Definition > ContainerFormer< E, Definition > + impl< E, Storage, Formed, Definition > CollectionFormer< E, Definition > where Definition : FormerDefinition< Context = (), Storage = Storage, Formed = Formed >, - Definition::Storage : ContainerAdd< Entry = E >, + Definition::Storage : CollectionAdd< Entry = E >, { - /// Constructs a new `ContainerFormer` instance, starting with an empty storage. + /// Constructs a new `CollectionFormer` instance, starting with an empty storage. /// This method serves as the entry point for the builder pattern, facilitating the - /// creation of a new container. + /// creation of a new collection. #[ inline( always ) ] pub fn new( end : Definition::End ) -> Self { @@ -454,10 +454,10 @@ pub( crate ) mod private } } - impl< E, Definition > ContainerFormer< E, Definition > + impl< E, Definition > CollectionFormer< E, Definition > where Definition : FormerDefinition, - Definition::Storage : ContainerAdd< Entry = E >, + Definition::Storage : CollectionAdd< Entry = E >, { /// Appends an entry to the end of the storage, expanding the internal collection. @@ -465,7 +465,7 @@ pub( crate ) mod private pub fn add< IntoElement >( mut self, entry : IntoElement ) -> Self where IntoElement : core::convert::Into< E >, { - ContainerAdd::add( &mut self.storage, entry.into() ); + CollectionAdd::add( &mut self.storage, entry.into() ); self } @@ -474,10 +474,10 @@ pub( crate ) mod private // impl< E, Definition > FormerBegin< Definition > - for ContainerFormer< E, Definition > + for CollectionFormer< E, Definition > where Definition : FormerDefinition, - Definition::Storage : ContainerAdd< Entry = E >, + Definition::Storage : CollectionAdd< Entry = E >, { #[ inline( always ) ] @@ -496,12 +496,22 @@ pub( crate ) mod private } -/// Former of a vector. -mod vector; +/// Former of a binary tree map. +mod btree_map; +/// Former of a binary tree set. +mod btree_set; +/// Former of a binary heap. +mod binary_heap; /// Former of a hash map. mod hash_map; /// Former of a hash set. mod hash_set; +/// Former of a linked list. +mod linked_list; +/// Former of a vector. +mod vector; +/// Former of a vector deque. +mod vector_deque; #[ doc( inline ) ] #[ allow( unused_imports ) ] @@ -537,13 +547,13 @@ pub mod exposed { EntryToVal, - ContainerValToEntry, + CollectionValToEntry, ValToEntry, - Container, - ContainerAdd, - ContainerAssign, - ContainerFormer, + Collection, + CollectionAdd, + CollectionAssign, + CollectionFormer, }; @@ -551,9 +561,14 @@ pub mod exposed #[ allow( unused_imports ) ] pub use super:: { - vector::*, + btree_map::*, + btree_set::*, + binary_heap::*, hash_map::*, hash_set::*, + linked_list::*, + vector::*, + vector_deque::*, }; } diff --git a/module/core/former_types/src/collection/binary_heap.rs b/module/core/former_types/src/collection/binary_heap.rs new file mode 100644 index 0000000000..6652fe0c4e --- /dev/null +++ b/module/core/former_types/src/collection/binary_heap.rs @@ -0,0 +1,255 @@ +//! This module provides a comprehensive approach to applying the builder pattern to `BinaryHeap` collections. +//! +//! By leveraging traits such as `Collection`, `CollectionAdd`, `CollectionAssign`, and `CollectionValToEntry`, +//! this module abstracts the operations on binary heap-like data structures, making them more flexible and easier to integrate as +//! as subformer, enabling fluid and intuitive manipulation of binary heaps via builder patterns. +//! + +use crate::*; +#[ allow( unused ) ] +use collection_tools::BinaryHeap; + +impl< E > Collection for BinaryHeap< E > +{ + type Entry = E; + type Val = E; + + #[ inline( always ) ] + fn entry_to_val( e : Self::Entry ) -> Self::Val + { + e + } + +} + +impl< E > CollectionAdd for BinaryHeap< E > +where + E : Ord +{ + + #[ inline( always ) ] + fn add( &mut self, e : Self::Entry ) -> bool + { + self.push( e ); + true + } + +} + +impl< E > CollectionAssign for BinaryHeap< E > +where + E : Ord +{ + #[ 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 > CollectionValToEntry< E > for BinaryHeap< E > +{ + type Entry = E; + #[ inline( always ) ] + fn val_to_entry( val : E ) -> Self::Entry + { + val + } +} + +// = storage + +impl< E > Storage +for BinaryHeap< E > +where + E : Ord +{ + type Preformed = BinaryHeap< E >; +} + +impl< E > StoragePreform +for BinaryHeap< E > +where + E : Ord +{ + fn preform( self ) -> Self::Preformed + { + self + } +} + +// = definition + +/// Represents the formation definition for a binary heap-like collection within the former framework. +/// +/// This structure defines the necessary parameters and relationships needed to form a binary heap-like collection, +/// 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 binary heap. +/// - `Context`: The context needed for the formation, can be provided externally. +/// - `Formed`: The type formed at the end of the formation process, typically a `BinaryHeap`. +/// - `End`: A trait determining the behavior at the end of the formation process. +/// + +#[ derive( Debug, Default ) ] +pub struct BinaryHeapDefinition< E, Context, Formed, End > +where + E : Ord, + End : FormingEnd< BinaryHeapDefinitionTypes< E, Context, Formed > >, +{ + _phantom : core::marker::PhantomData< ( E, Context, Formed, End ) >, +} + +impl< E, Context, Formed, End > FormerDefinition +for BinaryHeapDefinition< E, Context, Formed, End > +where + E : Ord, + End : FormingEnd< BinaryHeapDefinitionTypes< E, Context, Formed > >, +{ + type Storage = BinaryHeap< E >; + type Context = Context; + type Formed = Formed; + + type Types = BinaryHeapDefinitionTypes< E, Context, Formed >; + type End = End; +} + +// = definition type + +/// Holds the generic parameters for the `BinaryHeapDefinition`. +/// +/// This struct acts as a companion to `BinaryHeapDefinition`, 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 binary heap. +/// - `Context`: The context in which the binary heap is formed. +/// - `Formed`: The type produced as a result of the formation process. + +#[ derive( Debug, Default ) ] +pub struct BinaryHeapDefinitionTypes< E, Context = (), Formed = BinaryHeap< E > > +{ + _phantom : core::marker::PhantomData< ( E, Context, Formed ) >, +} + +impl< E, Context, Formed > FormerDefinitionTypes +for BinaryHeapDefinitionTypes< E, Context, Formed > +where + E : Ord +{ + type Storage = BinaryHeap< E >; + type Context = Context; + type Formed = Formed; +} + +// = mutator + +impl< E, Context, Formed > FormerMutator +for BinaryHeapDefinitionTypes< E, Context, Formed > +where + E : Ord +{ +} + +// = Entity To + +impl< E, Definition > EntityToFormer< Definition > +for BinaryHeap< E > +where + E : Ord, + Definition : FormerDefinition + < + Storage = BinaryHeap< E >, + Types = BinaryHeapDefinitionTypes + < + E, + < Definition as definition::FormerDefinition >::Context, + < Definition as definition::FormerDefinition >::Formed, + >, + >, + Definition::End : forming::FormingEnd< Definition::Types >, +{ + type Former = BinaryHeapFormer< E, Definition::Context, Definition::Formed, Definition::End >; +} + +impl< E > crate::EntityToStorage +for BinaryHeap< E > +{ + type Storage = BinaryHeap< E >; +} + +impl< E, Context, Formed, End > crate::EntityToDefinition< Context, Formed, End > +for BinaryHeap< E > +where + E : Ord, + End : crate::FormingEnd< BinaryHeapDefinitionTypes< E, Context, Formed > >, +{ + type Definition = BinaryHeapDefinition< E, Context, Formed, End >; + type Types = BinaryHeapDefinitionTypes< E, Context, Formed >; +} + +impl< E, Context, Formed > crate::EntityToDefinitionTypes< Context, Formed > +for BinaryHeap< E > +where + E : Ord +{ + type Types = BinaryHeapDefinitionTypes< E, Context, Formed >; +} + +// = subformer + +/// Provides a streamlined builder interface for constructing binary heap-like collections. +/// +/// `BinaryHeapFormer` is a type alias that configures the `CollectionFormer` for use specifically with binary heaps. +/// It integrates the `BinaryHeapDefinition` to facilitate the fluent and dynamic construction of binary heaps, leveraging +/// predefined settings to reduce boilerplate code. This approach enhances readability and simplifies the use of +/// binary heaps 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 binary heaps are repeatedly used or configured in similar ways across different +/// parts of an application. +/// + +pub type BinaryHeapFormer< E, Context, Formed, End > = +CollectionFormer::< E, BinaryHeapDefinition< E, Context, Formed, End > >; + +// = extension + +/// Provides an extension method for binary heaps to facilitate the use of the builder pattern. +/// +/// This trait extends the `BinaryHeap` type, enabling it to use the `BinaryHeapFormer` interface directly. +/// This allows for fluent, expressive construction and manipulation of binary heaps, integrating seamlessly +/// with the builder pattern provided by the `former` framework. It's a convenience trait that simplifies +/// creating configured binary heap builders with default settings. +/// +pub trait BinaryHeapExt< E > : sealed::Sealed +where + E : Ord +{ + /// Initializes a builder pattern for `BinaryHeap` using a default `BinaryHeapFormer`. + fn former() -> BinaryHeapFormer< E, (), BinaryHeap< E >, ReturnStorage >; +} + +impl< E > BinaryHeapExt< E > for BinaryHeap< E > +where + E : Ord +{ + fn former() -> BinaryHeapFormer< E, (), BinaryHeap< E >, ReturnStorage > + { + BinaryHeapFormer::< E, (), BinaryHeap< E >, ReturnStorage >::new( ReturnStorage::default() ) + } +} + +mod sealed +{ + pub trait Sealed {} + impl< E > Sealed for super::BinaryHeap< E > {} +} diff --git a/module/core/former_types/src/collection/btree_map.rs b/module/core/former_types/src/collection/btree_map.rs new file mode 100644 index 0000000000..d1d97bfde8 --- /dev/null +++ b/module/core/former_types/src/collection/btree_map.rs @@ -0,0 +1,252 @@ +//! This module provides a comprehensive approach to applying the builder pattern to `BTreeMap` collections. +//! +//! By leveraging traits such as `Collection`, `CollectionAdd`, `CollectionAssign`, and `CollectionValToEntry`, +//! this module abstracts the operations on binary tree map-like data structures, making them more flexible and easier to integrate as +//! as subformer, enabling fluid and intuitive manipulation of binary tree maps via builder patterns. +//! + +use crate::*; +use collection_tools::BTreeMap; + +impl< K, V > Collection for BTreeMap< K, V > +where + K : Ord, +{ + type Entry = ( K, V ); + type Val = V; + + #[ inline( always ) ] + fn entry_to_val( e : Self::Entry ) -> Self::Val + { + e.1 + } + +} + +impl< K, V > CollectionAdd for BTreeMap< K, V > +where + K : Ord, +{ + + #[ inline( always ) ] + fn add( &mut self, ( k, v ) : Self::Entry ) -> bool + { + self.insert( k, v ).map_or_else( || true, | _ | false ) + } + +} + +impl< K, V > CollectionAssign for BTreeMap< K, V > +where + K : Ord, +{ + + 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 BTreeMap< K, E > +where + K : Ord, +{ + type Preformed = BTreeMap< K, E >; +} + +impl< K, E > StoragePreform +for BTreeMap< K, E > +where + K : Ord, +{ + fn preform( self ) -> Self::Preformed + { + self + } +} + +// = definition + +/// Represents the formation definition for a hash map-like collection within the former framework. +/// +/// This structure defines the essential elements required to form a hash map-like collection, 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 `BTreeMap`. +/// - `End`: A trait defining the end behavior of the formation process, managing how the hash map is finalized. +/// + +#[ derive( Debug, Default ) ] +pub struct BTreeMapDefinition< K, E, Context = (), Formed = BTreeMap< K, E >, End = ReturnStorage > +where + K : Ord, + End : FormingEnd< BTreeMapDefinitionTypes< K, E, Context, Formed > >, +{ + _phantom : core::marker::PhantomData< ( K, E, Context, Formed, End ) >, +} + +impl< K, E, Context, Formed, End > FormerDefinition +for BTreeMapDefinition< K, E, Context, Formed, End > +where + K : Ord, + End : FormingEnd< BTreeMapDefinitionTypes< K, E, Context, Formed > >, +{ + + type Storage = BTreeMap< K, E >; + type Formed = Formed; + type Context = Context; + + type Types = BTreeMapDefinitionTypes< K, E, Context, Formed >; + type End = End; + +} + +// = definition types + +/// Holds the generic parameters for the `BTreeMapDefinition`. +/// +/// This companion struct to `BTreeMapDefinition` 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 `BTreeMap`. + +#[ derive( Debug, Default ) ] +pub struct BTreeMapDefinitionTypes< K, E, Context = (), Formed = BTreeMap< K, E > > +{ + _phantom : core::marker::PhantomData< ( K, E, Context, Formed ) >, +} + +impl< K, E, Context, Formed > FormerDefinitionTypes +for BTreeMapDefinitionTypes< K, E, Context, Formed > +where + K : Ord, +{ + type Storage = BTreeMap< K, E >; + type Formed = Formed; + type Context = Context; +} + +// = mutator + +impl< K, E, Context, Formed > FormerMutator +for BTreeMapDefinitionTypes< K, E, Context, Formed > +where + K : Ord, +{ +} + +// = Entity To + +impl< K, E, Definition > EntityToFormer< Definition > for BTreeMap< K, E > +where + K : Ord, + Definition : FormerDefinition + < + Storage = BTreeMap< K, E >, + Types = BTreeMapDefinitionTypes + < + K, + E, + < Definition as definition::FormerDefinition >::Context, + < Definition as definition::FormerDefinition >::Formed, + >, + >, + Definition::End : forming::FormingEnd< Definition::Types >, +{ + type Former = BTreeMapFormer< K, E, Definition::Context, Definition::Formed, Definition::End >; +} + +impl< K, E > crate::EntityToStorage +for BTreeMap< K, E > +where + K : Ord, +{ + type Storage = BTreeMap< K, E >; +} + +impl< K, E, Context, Formed, End > crate::EntityToDefinition< Context, Formed, End > +for BTreeMap< K, E > +where + K : Ord, + End : crate::FormingEnd< BTreeMapDefinitionTypes< K, E, Context, Formed > >, +{ + type Definition = BTreeMapDefinition< K, E, Context, Formed, End >; + type Types = BTreeMapDefinitionTypes< K, E, Context, Formed >; +} + +impl< K, E, Context, Formed > crate::EntityToDefinitionTypes< Context, Formed > +for BTreeMap< K, E > +where + K : Ord, +{ + type Types = BTreeMapDefinitionTypes< K, E, Context, Formed >; +} + +// = subformer + +/// Provides a streamlined builder interface for constructing hash map-like collections. +/// +/// `BTreeMapFormer` is a type alias that configures the `CollectionFormer` 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 `BTreeMapDefinition` 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 BTreeMapFormer< K, E, Context, Formed, End > = +CollectionFormer::< ( K, E ), BTreeMapDefinition< 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 `BTreeMap` type, enabling it to use the `BTreeMapFormer` 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 BTreeMapExt< K, E > : sealed::Sealed +where + K : Ord, +{ + /// Initializes a builder pattern for `BTreeMap` using a default `BTreeMapFormer`. + fn former() -> BTreeMapFormer< K, E, (), BTreeMap< K, E >, ReturnStorage >; +} + +impl< K, E > BTreeMapExt< K, E > for BTreeMap< K, E > +where + K : Ord, +{ + fn former() -> BTreeMapFormer< K, E, (), BTreeMap< K, E >, ReturnStorage > + { + BTreeMapFormer::< K, E, (), BTreeMap< K, E >, ReturnStorage >::new( ReturnStorage::default() ) + } +} + +mod sealed +{ + use super::BTreeMap; + pub trait Sealed {} + impl< K, E > Sealed for BTreeMap< K, E > {} +} diff --git a/module/core/former_types/src/collection/btree_set.rs b/module/core/former_types/src/collection/btree_set.rs new file mode 100644 index 0000000000..360c9484ae --- /dev/null +++ b/module/core/former_types/src/collection/btree_set.rs @@ -0,0 +1,243 @@ +//! This module provides a comprehensive approach to applying the builder pattern to `BTreeSet` collections. +//! +//! By leveraging traits such as `Collection`, `CollectionAdd`, `CollectionAssign`, and `CollectionValToEntry`, +//! this module abstracts the operations on binary tree set-like data structures, making them more flexible and easier to integrate as +//! as subformer, enabling fluid and intuitive manipulation of binary tree sets via builder patterns. +//! + +use crate::*; +#[ allow( unused ) ] +use collection_tools::BTreeSet; + +impl< E > Collection for BTreeSet< E > +{ + type Entry = E; + type Val = E; + + #[ inline( always ) ] + fn entry_to_val( e : Self::Entry ) -> Self::Val + { + e + } + +} + +impl< E > CollectionAdd for BTreeSet< E > +where + E : Ord +{ + + #[ inline( always ) ] + fn add( &mut self, e : Self::Entry ) -> bool + { + self.insert( e ); + true + } + +} + +impl< E > CollectionAssign for BTreeSet< E > +where + E : Ord +{ + #[ 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 > CollectionValToEntry< E > for BTreeSet< E > +where +{ + type Entry = E; + #[ inline( always ) ] + fn val_to_entry( val : E ) -> Self::Entry + { + val + } +} + +// = storage + +impl< E > Storage +for BTreeSet< E > +{ + type Preformed = BTreeSet< E >; +} + +impl< E > StoragePreform +for BTreeSet< E > +{ + fn preform( self ) -> Self::Preformed + { + self + } +} + +// = definition + +/// Represents the formation definition for a binary tree set-like collection within the former framework. +/// +/// This structure defines the necessary parameters and relationships needed to form a binary tree set-like collection, +/// 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 binary tree set. +/// - `Context`: The context needed for the formation, can be provided externally. +/// - `Formed`: The type formed at the end of the formation process, typically a `BTreeSet`. +/// - `End`: A trait determining the behavior at the end of the formation process. +/// + +#[ derive( Debug, Default ) ] +pub struct BTreeSetDefinition< E, Context, Formed, End > +where + End : FormingEnd< BTreeSetDefinitionTypes< E, Context, Formed > >, +{ + _phantom : core::marker::PhantomData< ( E, Context, Formed, End ) >, +} + +impl< E, Context, Formed, End > FormerDefinition +for BTreeSetDefinition< E, Context, Formed, End > +where + End : FormingEnd< BTreeSetDefinitionTypes< E, Context, Formed > >, +{ + type Storage = BTreeSet< E >; + type Context = Context; + type Formed = Formed; + + type Types = BTreeSetDefinitionTypes< E, Context, Formed >; + type End = End; +} + +// = definition type + +/// Holds the generic parameters for the `BTreeSetDefinition`. +/// +/// This struct acts as a companion to `BTreeSetDefinition`, 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 binary tree set. +/// - `Context`: The context in which the binary tree set is formed. +/// - `Formed`: The type produced as a result of the formation process. + +#[ derive( Debug, Default ) ] +pub struct BTreeSetDefinitionTypes< E, Context = (), Formed = BTreeSet< E > > +{ + _phantom : core::marker::PhantomData< ( E, Context, Formed ) >, +} + +impl< E, Context, Formed > FormerDefinitionTypes +for BTreeSetDefinitionTypes< E, Context, Formed > +{ + type Storage = BTreeSet< E >; + type Context = Context; + type Formed = Formed; +} + +// = mutator + +impl< E, Context, Formed > FormerMutator +for BTreeSetDefinitionTypes< E, Context, Formed > +{ +} + +// = Entity To + +impl< E, Definition > EntityToFormer< Definition > +for BTreeSet< E > +where + E : Ord, + Definition : FormerDefinition + < + Storage = BTreeSet< E >, + Types = BTreeSetDefinitionTypes + < + E, + < Definition as definition::FormerDefinition >::Context, + < Definition as definition::FormerDefinition >::Formed, + >, + >, + Definition::End : forming::FormingEnd< Definition::Types >, +{ + type Former = BTreeSetFormer< E, Definition::Context, Definition::Formed, Definition::End >; +} + +impl< E > crate::EntityToStorage +for BTreeSet< E > +{ + type Storage = BTreeSet< E >; +} + +impl< E, Context, Formed, End > crate::EntityToDefinition< Context, Formed, End > +for BTreeSet< E > +where + End : crate::FormingEnd< BTreeSetDefinitionTypes< E, Context, Formed > >, +{ + type Definition = BTreeSetDefinition< E, Context, Formed, End >; + type Types = BTreeSetDefinitionTypes< E, Context, Formed >; +} + +impl< E, Context, Formed > crate::EntityToDefinitionTypes< Context, Formed > +for BTreeSet< E > +{ + type Types = BTreeSetDefinitionTypes< E, Context, Formed >; +} + +// = subformer + +/// Provides a streamlined builder interface for constructing binary tree set-like collections. +/// +/// `BTreeSetFormer` is a type alias that configures the `CollectionFormer` for use specifically with binary tree sets. +/// It integrates the `BTreeSetDefinition` to facilitate the fluent and dynamic construction of binary tree sets, leveraging +/// predefined settings to reduce boilerplate code. This approach enhances readability and simplifies the use of +/// binary tree sets 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 binary tree sets are repeatedly used or configured in similar ways across different +/// parts of an application. +/// + +pub type BTreeSetFormer< E, Context, Formed, End > = +CollectionFormer::< E, BTreeSetDefinition< E, Context, Formed, End > >; + +// = extension + +/// Provides an extension method for binary tree sets to facilitate the use of the builder pattern. +/// +/// This trait extends the `BTreeSet` type, enabling it to use the `BTreeSetFormer` interface directly. +/// This allows for fluent, expressive construction and manipulation of binary tree sets, integrating seamlessly +/// with the builder pattern provided by the `former` framework. It's a convenience trait that simplifies +/// creating configured binary tree set builders with default settings. +/// +pub trait BTreeSetExt< E > : sealed::Sealed +where + E : Ord +{ + /// Initializes a builder pattern for `BTreeSet` using a default `BTreeSetFormer`. + fn former() -> BTreeSetFormer< E, (), BTreeSet< E >, ReturnStorage >; +} + +impl< E > BTreeSetExt< E > for BTreeSet< E > +where + E : Ord +{ + fn former() -> BTreeSetFormer< E, (), BTreeSet< E >, ReturnStorage > + { + BTreeSetFormer::< E, (), BTreeSet< E >, ReturnStorage >::new( ReturnStorage::default() ) + } +} + +mod sealed +{ + pub trait Sealed {} + impl< E > Sealed for super::BTreeSet< E > {} +} diff --git a/module/core/former/src/container/hash_map.rs b/module/core/former_types/src/collection/hash_map.rs similarity index 91% rename from module/core/former/src/container/hash_map.rs rename to module/core/former_types/src/collection/hash_map.rs index 6054850903..f6d6f1b58d 100644 --- a/module/core/former/src/container/hash_map.rs +++ b/module/core/former_types/src/collection/hash_map.rs @@ -1,6 +1,6 @@ -//! This module provides a comprehensive approach to applying the builder pattern to `HashMap` containers. +//! This module provides a comprehensive approach to applying the builder pattern to `HashMap` collections. //! -//! By leveraging traits such as `Container`, `ContainerAdd`, `ContainerAssign`, and `ContainerValToEntry`, +//! By leveraging traits such as `Collection`, `CollectionAdd`, `CollectionAssign`, and `CollectionValToEntry`, //! 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. //! @@ -8,7 +8,7 @@ use crate::*; use collection_tools::HashMap; -impl< K, V > Container for collection_tools::HashMap< K, V > +impl< K, V > Collection for HashMap< K, V > where K : core::cmp::Eq + core::hash::Hash, { @@ -23,7 +23,7 @@ where } -impl< K, V > ContainerAdd for collection_tools::HashMap< K, V > +impl< K, V > CollectionAdd for HashMap< K, V > where K : core::cmp::Eq + core::hash::Hash, { @@ -36,7 +36,7 @@ where } -impl< K, V > ContainerAssign for collection_tools::HashMap< K, V > +impl< K, V > CollectionAssign for HashMap< K, V > where K : core::cmp::Eq + core::hash::Hash, { @@ -74,9 +74,9 @@ where // = definition -/// Represents the formation definition for a hash map-like container within the former framework. +/// Represents the formation definition for a hash map-like collection within the former framework. /// -/// This structure defines the essential elements required to form a hash map-like container, detailing +/// This structure defines the essential elements required to form a hash map-like collection, 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. @@ -202,9 +202,9 @@ where // = subformer -/// Provides a streamlined builder interface for constructing hash map-like containers. +/// Provides a streamlined builder interface for constructing hash map-like collections. /// -/// `HashMapFormer` is a type alias that configures the `ContainerFormer` specifically for hash maps, +/// `HashMapFormer` is a type alias that configures the `CollectionFormer` 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 @@ -214,7 +214,7 @@ where /// a builder pattern both efficient and expressive. pub type HashMapFormer< K, E, Context, Formed, End > = -ContainerFormer::< ( K, E ), HashMapDefinition< K, E, Context, Formed, End > >; +CollectionFormer::< ( K, E ), HashMapDefinition< K, E, Context, Formed, End > >; // = extension diff --git a/module/core/former/src/container/hash_set.rs b/module/core/former_types/src/collection/hash_set.rs similarity index 85% rename from module/core/former/src/container/hash_set.rs rename to module/core/former_types/src/collection/hash_set.rs index 6e96684ee1..16d5dec6c0 100644 --- a/module/core/former/src/container/hash_set.rs +++ b/module/core/former_types/src/collection/hash_set.rs @@ -1,9 +1,9 @@ -//! 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. +//! This module provides a builder pattern implementation (`HashSetFormer`) for `HashSet`-like collections. 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 > +impl< K > Collection for HashSet< K > where K : core::cmp::Eq + core::hash::Hash, { @@ -18,7 +18,7 @@ where } -impl< K > ContainerAdd for collection_tools::HashSet< K > +impl< K > CollectionAdd for HashSet< K > where K : core::cmp::Eq + core::hash::Hash, { @@ -33,7 +33,7 @@ where } -impl< K > ContainerAssign for collection_tools::HashSet< K > +impl< K > CollectionAssign for HashSet< K > where K : core::cmp::Eq + core::hash::Hash, { @@ -49,7 +49,7 @@ where } } -impl< K > ContainerValToEntry< K > for HashSet< K > +impl< K > CollectionValToEntry< K > for HashSet< K > where K : core::cmp::Eq + core::hash::Hash, { @@ -61,7 +61,7 @@ where } } -// /// A trait for containers behaving like a `HashSet`, allowing insertion operations. +// /// A trait for collections 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. @@ -114,12 +114,12 @@ where // = definition -/// Represents the formation definition for a hash set-like container within the former framework. +/// Represents the formation definition for a hash set-like collection within the former framework. /// -/// This structure defines the essential elements required to form a hash set-like container, detailing +/// This structure defines the essential elements required to form a hash set-like collection, 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. +/// of hash set collections with dynamic characteristics and behaviors. /// /// # Type Parameters /// - `K`: The type of elements in the hash set. @@ -233,15 +233,15 @@ where // = subformer -/// Provides a concise alias for `ContainerFormer` configured specifically for `HashSet`-like containers. +/// Provides a concise alias for `CollectionFormer` configured specifically for `HashSet`-like collections. /// -/// `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. +/// `HashSetFormer` simplifies the creation of `HashSet` collections within builder patterns by leveraging +/// the `CollectionFormer` with predefined settings. This approach minimizes boilerplate code and enhances +/// readability, making it ideal for fluent and expressive construction of set collections within custom data structures. /// pub type HashSetFormer< K, Context, Formed, End > = -ContainerFormer::< K, HashSetDefinition< K, Context, Formed, End > >; +CollectionFormer::< K, HashSetDefinition< K, Context, Formed, End > >; // = extension diff --git a/module/core/former_types/src/collection/linked_list.rs b/module/core/former_types/src/collection/linked_list.rs new file mode 100644 index 0000000000..abdb327074 --- /dev/null +++ b/module/core/former_types/src/collection/linked_list.rs @@ -0,0 +1,234 @@ +//! This module provides a comprehensive approach to applying the builder pattern to `LinkedList` collections. +//! +//! By leveraging traits such as `Collection`, `CollectionAdd`, `CollectionAssign`, and `CollectionValToEntry`, +//! this module abstracts the operations on list-like data structures, making them more flexible and easier to integrate as +//! as subformer, enabling fluid and intuitive manipulation of lists via builder patterns. +//! + +use crate::*; +#[ allow( unused ) ] +use collection_tools::LinkedList; + +impl< E > Collection for LinkedList< E > +{ + type Entry = E; + type Val = E; + + #[ inline( always ) ] + fn entry_to_val( e : Self::Entry ) -> Self::Val + { + e + } + +} + +impl< E > CollectionAdd for LinkedList< E > +{ + + #[ inline( always ) ] + fn add( &mut self, e : Self::Entry ) -> bool + { + self.push_back( e ); + true + } + +} + +impl< E > CollectionAssign for LinkedList< 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 > CollectionValToEntry< E > for LinkedList< E > +where +{ + type Entry = E; + #[ inline( always ) ] + fn val_to_entry( val : E ) -> Self::Entry + { + val + } +} + +// = storage + +impl< E > Storage +for LinkedList< E > +{ + type Preformed = LinkedList< E >; +} + +impl< E > StoragePreform +for LinkedList< E > +{ + fn preform( self ) -> Self::Preformed + { + self + } +} + +// = definition + +/// Represents the formation definition for a list-like collection within the former framework. +/// +/// This structure defines the necessary parameters and relationships needed to form a list-like collection, +/// 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 list. +/// - `Context`: The context needed for the formation, can be provided externally. +/// - `Formed`: The type formed at the end of the formation process, typically a `LinkedList`. +/// - `End`: A trait determining the behavior at the end of the formation process. +/// + +#[ derive( Debug, Default ) ] +pub struct LinkedListDefinition< E, Context, Formed, End > +where + End : FormingEnd< LinkedListDefinitionTypes< E, Context, Formed > >, +{ + _phantom : core::marker::PhantomData< ( E, Context, Formed, End ) >, +} + +impl< E, Context, Formed, End > FormerDefinition +for LinkedListDefinition< E, Context, Formed, End > +where + End : FormingEnd< LinkedListDefinitionTypes< E, Context, Formed > >, +{ + type Storage = LinkedList< E >; + type Context = Context; + type Formed = Formed; + + type Types = LinkedListDefinitionTypes< E, Context, Formed >; + type End = End; +} + +// = definition type + +/// Holds the generic parameters for the `LinkedListDefinition`. +/// +/// This struct acts as a companion to `LinkedListDefinition`, 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 list. +/// - `Context`: The context in which the list is formed. +/// - `Formed`: The type produced as a result of the formation process. + +#[ derive( Debug, Default ) ] +pub struct LinkedListDefinitionTypes< E, Context = (), Formed = LinkedList< E > > +{ + _phantom : core::marker::PhantomData< ( E, Context, Formed ) >, +} + +impl< E, Context, Formed > FormerDefinitionTypes +for LinkedListDefinitionTypes< E, Context, Formed > +{ + type Storage = LinkedList< E >; + type Context = Context; + type Formed = Formed; +} + +// = mutator + +impl< E, Context, Formed > FormerMutator +for LinkedListDefinitionTypes< E, Context, Formed > +{ +} + +// = Entity To + +impl< E, Definition > EntityToFormer< Definition > +for LinkedList< E > +where + Definition : FormerDefinition + < + Storage = LinkedList< E >, + Types = LinkedListDefinitionTypes + < + E, + < Definition as definition::FormerDefinition >::Context, + < Definition as definition::FormerDefinition >::Formed, + >, + >, + Definition::End : forming::FormingEnd< Definition::Types >, +{ + type Former = LinkedListFormer< E, Definition::Context, Definition::Formed, Definition::End >; +} + +impl< E > crate::EntityToStorage +for LinkedList< E > +{ + type Storage = LinkedList< E >; +} + +impl< E, Context, Formed, End > crate::EntityToDefinition< Context, Formed, End > +for LinkedList< E > +where + End : crate::FormingEnd< LinkedListDefinitionTypes< E, Context, Formed > >, +{ + type Definition = LinkedListDefinition< E, Context, Formed, End >; + type Types = LinkedListDefinitionTypes< E, Context, Formed >; +} + +impl< E, Context, Formed > crate::EntityToDefinitionTypes< Context, Formed > +for LinkedList< E > +{ + type Types = LinkedListDefinitionTypes< E, Context, Formed >; +} + +// = subformer + +/// Provides a streamlined builder interface for constructing list-like collections. +/// +/// `LinkedListFormer` is a type alias that configures the `CollectionFormer` for use specifically with lists. +/// It integrates the `LinkedListDefinition` to facilitate the fluent and dynamic construction of lists, leveraging +/// predefined settings to reduce boilerplate code. This approach enhances readability and simplifies the use of +/// lists 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 lists are repeatedly used or configured in similar ways across different +/// parts of an application. +/// + +pub type LinkedListFormer< E, Context, Formed, End > = +CollectionFormer::< E, LinkedListDefinition< E, Context, Formed, End > >; + +// = extension + +/// Provides an extension method for lists to facilitate the use of the builder pattern. +/// +/// This trait extends the `LinkedList` type, enabling it to use the `LinkedListFormer` interface directly. +/// This allows for fluent, expressive construction and manipulation of lists, integrating seamlessly +/// with the builder pattern provided by the `former` framework. It's a convenience trait that simplifies +/// creating configured list builders with default settings. +/// +pub trait LinkedListExt< E > : sealed::Sealed +{ + /// Initializes a builder pattern for `LinkedList` using a default `LinkedListFormer`. + fn former() -> LinkedListFormer< E, (), LinkedList< E >, ReturnStorage >; +} + +impl< E > LinkedListExt< E > for LinkedList< E > +{ + fn former() -> LinkedListFormer< E, (), LinkedList< E >, ReturnStorage > + { + LinkedListFormer::< E, (), LinkedList< E >, ReturnStorage >::new( ReturnStorage::default() ) + } +} + +mod sealed +{ + pub trait Sealed {} + impl< E > Sealed for super::LinkedList< E > {} +} diff --git a/module/core/former/src/container/vector.rs b/module/core/former_types/src/collection/vector.rs similarity index 89% rename from module/core/former/src/container/vector.rs rename to module/core/former_types/src/collection/vector.rs index 33c344d1ab..96f7e577f1 100644 --- a/module/core/former/src/container/vector.rs +++ b/module/core/former_types/src/collection/vector.rs @@ -1,6 +1,6 @@ -//! This module provides a comprehensive approach to applying the builder pattern to `Vec` containers. +//! This module provides a comprehensive approach to applying the builder pattern to `Vec` collections. //! -//! By leveraging traits such as `Container`, `ContainerAdd`, `ContainerAssign`, and `ContainerValToEntry`, +//! By leveraging traits such as `Collection`, `CollectionAdd`, `CollectionAssign`, and `CollectionValToEntry`, //! 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. //! @@ -9,7 +9,7 @@ use crate::*; #[ allow( unused ) ] use collection_tools::Vec; -impl< E > Container for collection_tools::Vec< E > +impl< E > Collection for Vec< E > { type Entry = E; type Val = E; @@ -22,7 +22,7 @@ impl< E > Container for collection_tools::Vec< E > } -impl< E > ContainerAdd for collection_tools::Vec< E > +impl< E > CollectionAdd for Vec< E > { #[ inline( always ) ] @@ -34,7 +34,7 @@ impl< E > ContainerAdd for collection_tools::Vec< E > } -impl< E > ContainerAssign for collection_tools::Vec< E > +impl< E > CollectionAssign for Vec< E > { #[ inline( always ) ] fn assign< Elements >( &mut self, elements : Elements ) -> usize @@ -48,7 +48,7 @@ impl< E > ContainerAssign for collection_tools::Vec< E > } -impl< E > ContainerValToEntry< E > for collection_tools::Vec< E > +impl< E > CollectionValToEntry< E > for Vec< E > where { type Entry = E; @@ -78,9 +78,9 @@ for Vec< E > // = definition -/// Represents the formation definition for a vector-like container within the former framework. +/// Represents the formation definition for a vector-like collection within the former framework. /// -/// This structure defines the necessary parameters and relationships needed to form a vector-like container, +/// This structure defines the necessary parameters and relationships needed to form a vector-like collection, /// including its storage, context, the result of the formation process, and the behavior at the end of the formation. /// /// # Type Parameters @@ -189,9 +189,9 @@ for Vec< E > // = subformer -/// Provides a streamlined builder interface for constructing vector-like containers. +/// Provides a streamlined builder interface for constructing vector-like collections. /// -/// `VectorFormer` is a type alias that configures the `ContainerFormer` for use specifically with vectors. +/// `VectorFormer` is a type alias that configures the `CollectionFormer` 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. @@ -202,7 +202,7 @@ for Vec< E > /// pub type VectorFormer< E, Context, Formed, End > = -ContainerFormer::< E, VectorDefinition< E, Context, Formed, End > >; +CollectionFormer::< E, VectorDefinition< E, Context, Formed, End > >; // = extension diff --git a/module/core/former_types/src/collection/vector_deque.rs b/module/core/former_types/src/collection/vector_deque.rs new file mode 100644 index 0000000000..f3b08c6c01 --- /dev/null +++ b/module/core/former_types/src/collection/vector_deque.rs @@ -0,0 +1,234 @@ +//! This module provides a comprehensive approach to applying the builder pattern to `VecDeque` collections. +//! +//! By leveraging traits such as `Collection`, `CollectionAdd`, `CollectionAssign`, and `CollectionValToEntry`, +//! this module abstracts the operations on vector deque-like data structures, making them more flexible and easier to integrate as +//! as subformer, enabling fluid and intuitive manipulation of vector deques via builder patterns. +//! + +use crate::*; +#[ allow( unused ) ] +use collection_tools::VecDeque; + +impl< E > Collection for VecDeque< E > +{ + type Entry = E; + type Val = E; + + #[ inline( always ) ] + fn entry_to_val( e : Self::Entry ) -> Self::Val + { + e + } + +} + +impl< E > CollectionAdd for VecDeque< E > +{ + + #[ inline( always ) ] + fn add( &mut self, e : Self::Entry ) -> bool + { + self.push_back( e ); + true + } + +} + +impl< E > CollectionAssign for VecDeque< 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 > CollectionValToEntry< E > for VecDeque< E > +where +{ + type Entry = E; + #[ inline( always ) ] + fn val_to_entry( val : E ) -> Self::Entry + { + val + } +} + +// = storage + +impl< E > Storage +for VecDeque< E > +{ + type Preformed = VecDeque< E >; +} + +impl< E > StoragePreform +for VecDeque< E > +{ + fn preform( self ) -> Self::Preformed + { + self + } +} + +// = definition + +/// Represents the formation definition for a vector deque-like collection within the former framework. +/// +/// This structure defines the necessary parameters and relationships needed to form a vector deque-like collection, +/// 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 deque. +/// - `Context`: The context needed for the formation, can be provided externally. +/// - `Formed`: The type formed at the end of the formation process, typically a `VecDeque`. +/// - `End`: A trait determining the behavior at the end of the formation process. +/// + +#[ derive( Debug, Default ) ] +pub struct VecDequeDefinition< E, Context, Formed, End > +where + End : FormingEnd< VecDequeDefinitionTypes< E, Context, Formed > >, +{ + _phantom : core::marker::PhantomData< ( E, Context, Formed, End ) >, +} + +impl< E, Context, Formed, End > FormerDefinition +for VecDequeDefinition< E, Context, Formed, End > +where + End : FormingEnd< VecDequeDefinitionTypes< E, Context, Formed > >, +{ + type Storage = VecDeque< E >; + type Context = Context; + type Formed = Formed; + + type Types = VecDequeDefinitionTypes< E, Context, Formed >; + type End = End; +} + +// = definition type + +/// Holds the generic parameters for the `VecDequeDefinition`. +/// +/// This struct acts as a companion to `VecDequeDefinition`, 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 deque. +/// - `Context`: The context in which the vector deque is formed. +/// - `Formed`: The type produced as a result of the formation process. + +#[ derive( Debug, Default ) ] +pub struct VecDequeDefinitionTypes< E, Context = (), Formed = VecDeque< E > > +{ + _phantom : core::marker::PhantomData< ( E, Context, Formed ) >, +} + +impl< E, Context, Formed > FormerDefinitionTypes +for VecDequeDefinitionTypes< E, Context, Formed > +{ + type Storage = VecDeque< E >; + type Context = Context; + type Formed = Formed; +} + +// = mutator + +impl< E, Context, Formed > FormerMutator +for VecDequeDefinitionTypes< E, Context, Formed > +{ +} + +// = Entity To + +impl< E, Definition > EntityToFormer< Definition > +for VecDeque< E > +where + Definition : FormerDefinition + < + Storage = VecDeque< E >, + Types = VecDequeDefinitionTypes + < + E, + < Definition as definition::FormerDefinition >::Context, + < Definition as definition::FormerDefinition >::Formed, + >, + >, + Definition::End : forming::FormingEnd< Definition::Types >, +{ + type Former = VecDequeFormer< E, Definition::Context, Definition::Formed, Definition::End >; +} + +impl< E > crate::EntityToStorage +for VecDeque< E > +{ + type Storage = VecDeque< E >; +} + +impl< E, Context, Formed, End > crate::EntityToDefinition< Context, Formed, End > +for VecDeque< E > +where + End : crate::FormingEnd< VecDequeDefinitionTypes< E, Context, Formed > >, +{ + type Definition = VecDequeDefinition< E, Context, Formed, End >; + type Types = VecDequeDefinitionTypes< E, Context, Formed >; +} + +impl< E, Context, Formed > crate::EntityToDefinitionTypes< Context, Formed > +for VecDeque< E > +{ + type Types = VecDequeDefinitionTypes< E, Context, Formed >; +} + +// = subformer + +/// Provides a streamlined builder interface for constructing vector deque-like collections. +/// +/// `VecDequeFormer` is a type alias that configures the `CollectionFormer` for use specifically with vector deques. +/// It integrates the `VecDequeDefinition` to facilitate the fluent and dynamic construction of vector deques, leveraging +/// predefined settings to reduce boilerplate code. This approach enhances readability and simplifies the use of +/// vector deques 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 vector deques are repeatedly used or configured in similar ways across different +/// parts of an application. +/// + +pub type VecDequeFormer< E, Context, Formed, End > = +CollectionFormer::< E, VecDequeDefinition< E, Context, Formed, End > >; + +// = extension + +/// Provides an extension method for vector deques to facilitate the use of the builder pattern. +/// +/// This trait extends the `VecDeque` type, enabling it to use the `VecDequeFormer` interface directly. +/// This allows for fluent, expressive construction and manipulation of vector deques, integrating seamlessly +/// with the builder pattern provided by the `former` framework. It's a convenience trait that simplifies +/// creating configured vector deque builders with default settings. +/// +pub trait VecDequeExt< E > : sealed::Sealed +{ + /// Initializes a builder pattern for `VecDeque` using a default `VecDequeFormer`. + fn former() -> VecDequeFormer< E, (), VecDeque< E >, ReturnStorage >; +} + +impl< E > VecDequeExt< E > for VecDeque< E > +{ + fn former() -> VecDequeFormer< E, (), VecDeque< E >, ReturnStorage > + { + VecDequeFormer::< E, (), VecDeque< E >, ReturnStorage >::new( ReturnStorage::default() ) + } +} + +mod sealed +{ + pub trait Sealed {} + impl< E > Sealed for super::VecDeque< E > {} +} diff --git a/module/core/former_types/src/component.rs b/module/core/former_types/src/component.rs new file mode 100644 index 0000000000..21398497d8 --- /dev/null +++ b/module/core/former_types/src/component.rs @@ -0,0 +1,198 @@ +/// Provides a generic interface for setting a component of a certain type on an object. +/// +/// This trait abstracts the action of setting or replacing a component, where a component +/// can be any part or attribute of an object, such as a field value. It is designed to be +/// generic over the type of the component being set (`T`) and the type that can be converted +/// into the component (`IntoT`). This design allows for flexible implementations that can +/// accept various types that can then be converted into the required component type. +/// +/// # Type Parameters +/// +/// - `T`: The type of the component to be set on the implementing object. This type represents +/// the final form of the component as it should be stored or represented in the object. +/// - `IntoT`: The type that can be converted into `T`. This allows the `assign` method to accept +/// different types that are capable of being transformed into the required component type `T`, +/// providing greater flexibility in setting the component. +/// +/// # Examples +/// +/// Implementing `Assign` to set a name string on a struct: +/// +/// ```rust +/// use former_types::Assign; // use crate `former` instead of crate `former_types` unless you need to use crate `former_types` directly +/// +/// struct MyStruct { +/// name: String, +/// } +/// +/// impl< IntoT : Into< String > > Assign< String, IntoT > for MyStruct +/// { +/// fn assign( &mut self, component : IntoT ) +/// { +/// self.name = component.into(); +/// } +/// } +/// +/// let mut obj = MyStruct { name : String::new() }; +/// obj.assign( "New Name" ); +/// assert_eq!( obj.name, "New Name" ); +/// ``` +#[ cfg( any( feature = "types_component_assign" ) ) ] +pub trait Assign< T, IntoT > +where + IntoT : Into< T >, +{ + /// Sets or replaces the component on the object with the given value. + /// + /// This method takes ownership of the given value (`component`), which is of type `IntoT`. + /// `component` is then converted into type `T` and set as the component of the object. + fn assign( &mut self, component : IntoT ); +} + +/// Extension trait to provide a method for setting a component on an `Option` +/// if the `Option` is currently `None`. If the `Option` is `Some`, the method will +/// delegate to the `Assign` trait's `assign` method. +/// +/// # Type Parameters +/// +/// - `T`: The type of the component to be set on the implementing object. This type represents +/// the final form of the component as it should be stored or represented in the object. +/// +/// # Examples +/// +/// Using `option_assign` to set a component on an `Option`: +/// +/// ```rust +/// use former_types::{ Assign, OptionExt }; // use crate `former` instead of crate `former_types` unless you need to use crate `former_types` directly +/// +/// struct MyStruct +/// { +/// name : String, +/// } +/// +/// impl< IntoT : Into< MyStruct > > Assign< MyStruct, IntoT > for MyStruct +/// { +/// fn assign( &mut self, component : IntoT ) +/// { +/// self.name = component.into().name; +/// } +/// } +/// +/// let mut opt_struct: Option< MyStruct > = None; +/// opt_struct.option_assign( MyStruct { name: "New Name".to_string() } ); +/// assert_eq!( opt_struct.unwrap().name, "New Name" ); +/// ``` +#[ cfg( any( feature = "types_component_assign" ) ) ] +pub trait OptionExt< T > : sealed::Sealed +where + T : Sized + Assign< T, T >, +{ + /// Sets the component on the `Option` if it is `None`. + /// + /// If the `Option` is `Some`, the `assign` method is called to update the existing value. + /// + /// # Parameters + /// + /// - `src`: The value to assign to the `Option`. + fn option_assign( & mut self, src : T ); +} + +#[ cfg( any( feature = "types_component_assign" ) ) ] +impl< T > OptionExt< T > for Option< T > +where + T : Sized + Assign< T, T >, +{ + #[ inline( always ) ] + fn option_assign( & mut self, src : T ) + { + match self + { + Some( self_ref ) => Assign::assign( self_ref, Into::< T >::into( src ) ), + None => * self = Some( src ), + } + } +} + +#[ cfg( any( feature = "types_component_assign" ) ) ] +mod sealed +{ + pub trait Sealed {} + impl< T > Sealed for Option< T > + where + T : Sized + super::Assign< T, T >, + {} +} + +/// The `AssignWithType` trait provides a mechanism to set a component on an object, +/// utilizing the type information explicitly. This trait extends the functionality of `Assign` +/// by allowing implementers to specify the component's type at the method call site, +/// enhancing expressiveness in code that manipulates object states. +/// +/// # Type Parameters +/// +/// - `T`: The type of the component to be set on the implementing object. This specifies +/// the exact type expected by the object as its component. +/// - `IntoT`: A type that can be converted into `T`, providing flexibility in the types of values +/// that can be used to set the component. +/// +/// # Examples +/// +/// Implementing `AssignWithType` to set a username on a struct: +/// +/// ```rust +/// use former_types::{ Assign, AssignWithType }; // use crate `former` instead of crate `former_types` unless you need to use crate `former_types` directly +/// +/// struct UserProfile +/// { +/// username : String, +/// } +/// +/// impl< IntoT : Into< String > > Assign< String, IntoT > for UserProfile +/// { +/// fn assign( &mut self, component : IntoT ) +/// { +/// self.username = component.into(); +/// } +/// } +/// +/// let mut user_profile = UserProfile { username : String::new() }; +/// user_profile.assign_with_type::< String, _ >("john_doe"); +/// +/// assert_eq!( user_profile.username, "john_doe" ); +/// ``` +#[ cfg( any( feature = "types_component_assign" ) ) ] +pub trait AssignWithType +{ + /// Sets the value of a component by its type. + /// + /// This method allows an implementer of `AssignWithType` to set a component on `self` + /// where the component's type is `T`, and the input value is of type `IntoT`, which can be + /// converted into `T`. This method bridges the gap between dynamic type usage and static type + /// enforcement, providing a flexible yet type-safe interface for modifying object states. + /// + /// # Parameters + /// + /// - `component`: The value to assign to the component. + /// + /// # Type Parameters + /// + /// - `T`: The type of the component to be set on the implementing object. + /// - `IntoT`: A type that can be converted into `T`. + fn assign_with_type< T, IntoT >( & mut self, component : IntoT ) + where + IntoT : Into< T >, + Self : Assign< T, IntoT >; +} + +#[ cfg( any( feature = "types_component_assign" ) ) ] +impl< S > AssignWithType for S +{ + #[ inline( always ) ] + fn assign_with_type< T, IntoT >( & mut self, component : IntoT ) + where + IntoT : Into< T >, + Self : Assign< T, IntoT >, + { + Assign::< T, IntoT >::assign( self, component ); + } +} diff --git a/module/core/former/src/definition.rs b/module/core/former_types/src/definition.rs similarity index 100% rename from module/core/former/src/definition.rs rename to module/core/former_types/src/definition.rs diff --git a/module/core/former/src/forming.rs b/module/core/former_types/src/forming.rs similarity index 97% rename from module/core/former/src/forming.rs rename to module/core/former_types/src/forming.rs index 892f4ad526..d95bea8666 100644 --- a/module/core/former/src/forming.rs +++ b/module/core/former_types/src/forming.rs @@ -57,7 +57,7 @@ where /// 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. +/// - `Storage`: The type of the collection being processed. /// - `Context`: The type of the context that might be altered or returned upon completion. pub trait FormingEnd< Definition : crate::FormerDefinitionTypes > @@ -65,7 +65,7 @@ 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. + /// - `collection`: The collection being processed. /// - `context`: Optional context to be transformed or returned. /// /// # Returns @@ -85,9 +85,9 @@ where } } -/// A `FormingEnd` implementation that directly returns the formed container as the final product of the forming process. +/// A `FormingEnd` implementation that directly returns the formed collection 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, +/// This struct is particularly useful when the end result of the forming process is simply the formed collection 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 ) ] @@ -193,7 +193,7 @@ impl< Definition : crate::FormerDefinitionTypes > FormingEndClosure< Definition /// /// # Parameters /// - /// * `closure` - A closure that matches the expected signature for transforming a container + /// * `closure` - A closure that matches the expected signature for transforming a collection /// and context into a new context. This closure is stored and called by the /// `call` method of the `FormingEnd` trait implementation. /// diff --git a/module/core/former_types/src/lib.rs b/module/core/former_types/src/lib.rs new file mode 100644 index 0000000000..51cfc78eab --- /dev/null +++ b/module/core/former_types/src/lib.rs @@ -0,0 +1,101 @@ +#![ cfg_attr( feature = "no_std", no_std ) ] +#![ doc( html_logo_url = "https://raw.githubusercontent.com/Wandalen/wTools/master/asset/img/logo_v3_trans_square.png" ) ] +#![ doc( html_favicon_url = "https://raw.githubusercontent.com/Wandalen/wTools/alpha/asset/img/logo_v3_trans_square_icon_small_v2.ico" ) ] +#![ doc( html_root_url = "https://docs.rs/former_types/latest/former_types/" ) ] +#![ doc = include_str!( concat!( env!( "CARGO_MANIFEST_DIR" ), "/", "Readme.md" ) ) ] + +/// Axiomatic things. +#[ cfg( feature = "enabled" ) ] +#[ cfg( feature = "derive_former" ) ] +mod axiomatic; +/// Definition of former. +#[ cfg( feature = "enabled" ) ] +#[ cfg( feature = "derive_former" ) ] +mod definition; +/// Forming process. +#[ cfg( feature = "enabled" ) ] +#[ cfg( feature = "derive_former" ) ] +mod forming; +/// Storage. +#[ cfg( feature = "enabled" ) ] +#[ cfg( feature = "derive_former" ) ] +mod storage; + +/// Interface for collections. +#[ cfg( feature = "enabled" ) ] +#[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] +#[ cfg( feature = "derive_former" ) ] +mod collection; + +/// Component-based forming. +#[ cfg( feature = "enabled" ) ] +#[ cfg( any( feature = "types_component_assign" ) ) ] +mod component; + +/// Namespace with dependencies. +#[ cfg( feature = "enabled" ) ] +pub mod dependency +{ + pub use ::collection_tools; +} + +#[ doc( inline ) ] +#[ allow( unused_imports ) ] +#[ cfg( feature = "enabled" ) ] +pub use protected::*; + +/// Protected namespace of the module. +#[ cfg( feature = "enabled" ) ] +pub mod protected +{ + #[ doc( inline ) ] + #[ allow( unused_imports ) ] + pub use super::orphan::*; +} + +/// Parented namespace of the module. +#[ cfg( feature = "enabled" ) ] +pub mod orphan +{ + #[ doc( inline ) ] + #[ allow( unused_imports ) ] + pub use super::exposed::*; +} + +/// Exposed namespace of the module. +#[ cfg( feature = "enabled" ) ] +pub mod exposed +{ + + #[ doc( inline ) ] + #[ allow( unused_imports ) ] + pub use super::prelude::*; + + #[ doc( inline ) ] + #[ allow( unused_imports ) ] + #[ cfg( feature = "derive_former" ) ] + pub use super:: + { + axiomatic::*, + definition::*, + forming::*, + storage::*, + }; + + #[ doc( inline ) ] + #[ allow( unused_imports ) ] + #[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] + #[ cfg( feature = "derive_former" ) ] + pub use super::collection::*; + +} + +/// Prelude to use essentials: `use my_module::prelude::*`. +#[ cfg( feature = "enabled" ) ] +pub mod prelude +{ + #[ doc( inline ) ] + #[ allow( unused_imports ) ] + #[ cfg( any( feature = "types_component_assign" ) ) ] + pub use super::component::*; +} diff --git a/module/core/former/src/storage.rs b/module/core/former_types/src/storage.rs similarity index 100% rename from module/core/former/src/storage.rs rename to module/core/former_types/src/storage.rs diff --git a/module/core/former_types/tests/inc/mod.rs b/module/core/former_types/tests/inc/mod.rs new file mode 100644 index 0000000000..59fea4b027 --- /dev/null +++ b/module/core/former_types/tests/inc/mod.rs @@ -0,0 +1,51 @@ +// #![ deny( missing_docs ) ] + +#[ allow( unused_imports ) ] +use super::*; + +#[ cfg( feature = "derive_former" ) ] +#[ path = "../../../former/tests/inc/former_tests" ] +mod former_tests +{ + #[ allow( unused_imports ) ] + use super::*; + + // = basic + + #[ cfg( any( feature = "use_alloc", not( feature = "no_std" ) ) ) ] + mod a_basic_manual; + mod a_primitives_manual; + + #[ cfg( any( feature = "use_alloc", not( feature = "no_std" ) ) ) ] + mod subform_collection_basic_manual; + + // = parametrization + + #[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] + mod parametrized_struct_manual; + mod parametrized_slice_manual; + +} + +#[ cfg( feature = "types_components" ) ] +#[ path = "../../../former/tests/inc/components_tests" ] +mod components_tests +{ + use super::*; + + #[ cfg( feature = "types_component_from" ) ] + mod component_from_manual; + + #[ cfg( feature = "types_component_assign" ) ] + mod component_assign_manual; + + #[ cfg( all( feature = "types_component_assign" ) ) ] + mod components_assign_manual; + + // #[ cfg( all( feature = "derive_from_components" ) ) ] + mod from_components_manual; + + #[ cfg( all( feature = "types_component_assign" ) ) ] + mod composite_manual; + +} diff --git a/module/core/former_types/tests/smoke_test.rs b/module/core/former_types/tests/smoke_test.rs new file mode 100644 index 0000000000..7fd288e61d --- /dev/null +++ b/module/core/former_types/tests/smoke_test.rs @@ -0,0 +1,14 @@ + +// #[ cfg( feature = "default" ) ] +#[ test ] +fn local_smoke_test() +{ + ::test_tools::smoke_test_for_local_run(); +} + +// #[ cfg( feature = "default" ) ] +#[ test ] +fn published_smoke_test() +{ + ::test_tools::smoke_test_for_published_run(); +} diff --git a/module/core/former_types/tests/tests.rs b/module/core/former_types/tests/tests.rs new file mode 100644 index 0000000000..caea26275c --- /dev/null +++ b/module/core/former_types/tests/tests.rs @@ -0,0 +1,12 @@ + +include!( "../../../../module/step/meta/src/module/terminal.rs" ); + +#[ allow( unused_imports ) ] +use test_tools::exposed::*; +#[ allow( unused_imports ) ] +use former_types as the_module; +#[ allow( unused_imports ) ] +use former_types as former; + +#[ cfg( feature = "enabled" ) ] +mod inc; diff --git a/module/core/implements/Readme.md b/module/core/implements/Readme.md index 8fa1c95174..8fe784a119 100644 --- a/module/core/implements/Readme.md +++ b/module/core/implements/Readme.md @@ -2,7 +2,7 @@ # Module :: implements - [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/module_implements_push.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/module_implements_push.yml) [![docs.rs](https://img.shields.io/docsrs/implements?color=e3e8f0&logo=docs.rs)](https://docs.rs/implements) [![Open in Gitpod](https://raster.shields.io/static/v1?label=try&message=online&color=eee&logo=gitpod&logoColor=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module/core/implements/examples/implements_trivial.rs,RUN_POSTFIX=--example%20/home/sakapoi/Документи/wTools_fork/module/core/implements/examples/implements_trivial/https://github.com/Wandalen/wTools) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) + [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/module_implements_push.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/module_implements_push.yml) [![docs.rs](https://img.shields.io/docsrs/implements?color=e3e8f0&logo=docs.rs)](https://docs.rs/implements) [![Open in Gitpod](https://raster.shields.io/static/v1?label=try&message=online&color=eee&logo=gitpod&logoColor=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fcore%2Fimplements%2Fexamples%2Fimplements_trivial.rs,RUN_POSTFIX=--example%20implements_trivial/https://github.com/Wandalen/wTools) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) Macro to answer the question: does it implement a trait? diff --git a/module/core/impls_index/Readme.md b/module/core/impls_index/Readme.md index f8a249942e..39573c49bd 100644 --- a/module/core/impls_index/Readme.md +++ b/module/core/impls_index/Readme.md @@ -2,7 +2,7 @@ # Module :: impls_index - [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/module_impls_index_push.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/module_impls_index_push.yml) [![docs.rs](https://img.shields.io/docsrs/impls_index?color=e3e8f0&logo=docs.rs)](https://docs.rs/impls_index) [![Open in Gitpod](https://raster.shields.io/static/v1?label=try&message=online&color=eee&logo=gitpod&logoColor=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module/core/impls_index/examples/impls_index_trivial.rs,RUN_POSTFIX=--example%20/home/sakapoi/Документи/wTools_fork/module/core/impls_index/examples/impls_index_trivial/https://github.com/Wandalen/wTools) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) + [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/module_impls_index_push.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/module_impls_index_push.yml) [![docs.rs](https://img.shields.io/docsrs/impls_index?color=e3e8f0&logo=docs.rs)](https://docs.rs/impls_index) [![Open in Gitpod](https://raster.shields.io/static/v1?label=try&message=online&color=eee&logo=gitpod&logoColor=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fcore%2Fimpls_index%2Fexamples%2Fimpls_index_trivial.rs,RUN_POSTFIX=--example%20impls_index_trivial/https://github.com/Wandalen/wTools) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) Several of macros to put each function under a named macro to index every function in a class. diff --git a/module/core/impls_index_meta/src/impls.rs b/module/core/impls_index_meta/src/impls.rs index c2f9425fed..1ae6c3ee9b 100644 --- a/module/core/impls_index_meta/src/impls.rs +++ b/module/core/impls_index_meta/src/impls.rs @@ -1,5 +1,5 @@ -use macro_tools::{ Result, Many }; +use macro_tools::{ Result, Many, AsMuchAsPossibleNoDelimiter }; use macro_tools::prelude::*; /// diff --git a/module/core/inspect_type/Readme.md b/module/core/inspect_type/Readme.md index 527b31d3fb..e45df40b25 100644 --- a/module/core/inspect_type/Readme.md +++ b/module/core/inspect_type/Readme.md @@ -2,7 +2,7 @@ # Module :: inspect_type - [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/module_inspect_type_push.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/module_inspect_type_push.yml) [![docs.rs](https://img.shields.io/docsrs/inspect_type?color=e3e8f0&logo=docs.rs)](https://docs.rs/inspect_type) [![Open in Gitpod](https://raster.shields.io/static/v1?label=try&message=online&color=eee&logo=gitpod&logoColor=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module/core/inspect_type/examples/inspect_type_trivial.rs,RUN_POSTFIX=--example%20/home/sakapoi/Документи/wTools_fork/module/core/inspect_type/examples/inspect_type_trivial/https://github.com/Wandalen/wTools) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) + [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/module_inspect_type_push.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/module_inspect_type_push.yml) [![docs.rs](https://img.shields.io/docsrs/inspect_type?color=e3e8f0&logo=docs.rs)](https://docs.rs/inspect_type) [![Open in Gitpod](https://raster.shields.io/static/v1?label=try&message=online&color=eee&logo=gitpod&logoColor=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fcore%2Finspect_type%2Fexamples%2Finspect_type_trivial.rs,RUN_POSTFIX=--example%20inspect_type_trivial/https://github.com/Wandalen/wTools) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) Diagnostic-purpose tools to inspect type of a variable and its size. diff --git a/module/core/interval_adapter/Cargo.toml b/module/core/interval_adapter/Cargo.toml index 218f3b9d96..f08b7f12a0 100644 --- a/module/core/interval_adapter/Cargo.toml +++ b/module/core/interval_adapter/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "interval_adapter" -version = "0.19.0" +version = "0.21.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", diff --git a/module/core/interval_adapter/Readme.md b/module/core/interval_adapter/Readme.md index 733185aeeb..19cfc05f9e 100644 --- a/module/core/interval_adapter/Readme.md +++ b/module/core/interval_adapter/Readme.md @@ -2,7 +2,7 @@ # Module :: interval_adapter - [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/module_interval_adapter_push.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/module_interval_adapter_push.yml) [![docs.rs](https://img.shields.io/docsrs/interval_adapter?color=e3e8f0&logo=docs.rs)](https://docs.rs/interval_adapter) [![Open in Gitpod](https://raster.shields.io/static/v1?label=try&message=online&color=eee&logo=gitpod&logoColor=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module/core/interval_adapter/examples/interval_adapter_trivial.rs,RUN_POSTFIX=--example%20/home/sakapoi/Документи/wTools_fork/module/core/interval_adapter/examples/interval_adapter_trivial/https://github.com/Wandalen/wTools) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) + [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/module_interval_adapter_push.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/module_interval_adapter_push.yml) [![docs.rs](https://img.shields.io/docsrs/interval_adapter?color=e3e8f0&logo=docs.rs)](https://docs.rs/interval_adapter) [![Open in Gitpod](https://raster.shields.io/static/v1?label=try&message=online&color=eee&logo=gitpod&logoColor=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fcore%2Finterval_adapter%2Fexamples%2Finterval_adapter_trivial.rs,RUN_POSTFIX=--example%20interval_adapter_trivial/https://github.com/Wandalen/wTools) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) Integer interval adapter for both Range and RangeInclusive. diff --git a/module/core/interval_adapter/tests/inc/mod.rs b/module/core/interval_adapter/tests/inc/mod.rs index b46d9099d5..0014f67a76 100644 --- a/module/core/interval_adapter/tests/inc/mod.rs +++ b/module/core/interval_adapter/tests/inc/mod.rs @@ -31,6 +31,8 @@ tests_impls! let got = [ 0, 3 ].into_interval(); a_id!( got, exp ); + // assert( false ); + } // diff --git a/module/core/is_slice/Readme.md b/module/core/is_slice/Readme.md index a2eeea5825..cd6d9eadac 100644 --- a/module/core/is_slice/Readme.md +++ b/module/core/is_slice/Readme.md @@ -2,7 +2,7 @@ # Module :: is_slice - [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/module_is_slice_push.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/module_is_slice_push.yml) [![docs.rs](https://img.shields.io/docsrs/is_slice?color=e3e8f0&logo=docs.rs)](https://docs.rs/is_slice) [![Open in Gitpod](https://raster.shields.io/static/v1?label=try&message=online&color=eee&logo=gitpod&logoColor=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module/core/is_slice/examples/is_slice_trivial.rs,RUN_POSTFIX=--example%20/home/sakapoi/Документи/wTools_fork/module/core/is_slice/examples/is_slice_trivial/https://github.com/Wandalen/wTools) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) + [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/module_is_slice_push.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/module_is_slice_push.yml) [![docs.rs](https://img.shields.io/docsrs/is_slice?color=e3e8f0&logo=docs.rs)](https://docs.rs/is_slice) [![Open in Gitpod](https://raster.shields.io/static/v1?label=try&message=online&color=eee&logo=gitpod&logoColor=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fcore%2Fis_slice%2Fexamples%2Fis_slice_trivial.rs,RUN_POSTFIX=--example%20is_slice_trivial/https://github.com/Wandalen/wTools) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) Macro to answer the question: is it a slice? diff --git a/module/core/iter_tools/Cargo.toml b/module/core/iter_tools/Cargo.toml index 077198d0ce..233ac33f5b 100644 --- a/module/core/iter_tools/Cargo.toml +++ b/module/core/iter_tools/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "iter_tools" -version = "0.16.0" +version = "0.17.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", diff --git a/module/core/iter_tools/Readme.md b/module/core/iter_tools/Readme.md index 30ef42b2d2..4aaebd7c0f 100644 --- a/module/core/iter_tools/Readme.md +++ b/module/core/iter_tools/Readme.md @@ -2,7 +2,7 @@ # Module :: iter_tools - [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/module_iter_tools_push.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/module_iter_tools_push.yml) [![docs.rs](https://img.shields.io/docsrs/iter_tools?color=e3e8f0&logo=docs.rs)](https://docs.rs/iter_tools) [![Open in Gitpod](https://raster.shields.io/static/v1?label=try&message=online&color=eee&logo=gitpod&logoColor=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module/core/iter_tools/examples/iter_tools_trivial.rs,RUN_POSTFIX=--example%20/home/sakapoi/Документи/wTools_fork/module/core/iter_tools/examples/iter_tools_trivial/https://github.com/Wandalen/wTools) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) + [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/module_iter_tools_push.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/module_iter_tools_push.yml) [![docs.rs](https://img.shields.io/docsrs/iter_tools?color=e3e8f0&logo=docs.rs)](https://docs.rs/iter_tools) [![Open in Gitpod](https://raster.shields.io/static/v1?label=try&message=online&color=eee&logo=gitpod&logoColor=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fcore%2Fiter_tools%2Fexamples%2Fiter_tools_trivial.rs,RUN_POSTFIX=--example%20iter_tools_trivial/https://github.com/Wandalen/wTools) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) Collection of general purpose tools to iterate. Currently it simply reexports itertools. diff --git a/module/core/macro_tools/Cargo.toml b/module/core/macro_tools/Cargo.toml index f52852c399..d6d8ce9d7b 100644 --- a/module/core/macro_tools/Cargo.toml +++ b/module/core/macro_tools/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "macro_tools" -version = "0.24.0" +version = "0.27.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", @@ -28,7 +28,7 @@ all-features = false [features] default = [ "enabled" ] full = [ "enabled" ] -enabled = [] +enabled = [ "former_types/enabled", "interval_adapter/enabled" ] # qqq : put all files under features: macro_attr, macro_container_kind, ... @@ -39,9 +39,12 @@ proc-macro2 = { version = "~1.0.78", features = [] } quote = { version = "~1.0.35", features = [] } syn = { version = "~2.0.52", features = [ "full", "extra-traits" ] } +# qqq : optimize features list + ## internal -interval_adapter = { workspace = true, features = [ "default" ] } -# strs_tools = { workspace = true, features = [ "default" ] } +interval_adapter = { workspace = true, features = [] } +former_types = { workspace = true, features = [ "types_component_assign" ] } [dev-dependencies] test_tools = { workspace = true } +const_format = { version = "0.2.32" } diff --git a/module/core/macro_tools/Readme.md b/module/core/macro_tools/Readme.md index 98396be845..62e2e0013a 100644 --- a/module/core/macro_tools/Readme.md +++ b/module/core/macro_tools/Readme.md @@ -2,32 +2,348 @@ # Module :: proc_macro_tools - [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/module_macro_tools_push.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/module_macro_tools_push.yml) [![docs.rs](https://img.shields.io/docsrs/macro_tools?color=e3e8f0&logo=docs.rs)](https://docs.rs/macro_tools) [![Open in Gitpod](https://raster.shields.io/static/v1?label=try&message=online&color=eee&logo=gitpod&logoColor=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module/core/macro_tools/examples/macro_tools_trivial.rs,RUN_POSTFIX=--example%20/home/sakapoi/Документи/wTools_fork/module/core/macro_tools/examples/macro_tools_trivial/https://github.com/Wandalen/wTools) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) + [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/module_macro_tools_push.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/module_macro_tools_push.yml) [![docs.rs](https://img.shields.io/docsrs/macro_tools?color=e3e8f0&logo=docs.rs)](https://docs.rs/macro_tools) [![Open in Gitpod](https://raster.shields.io/static/v1?label=try&message=online&color=eee&logo=gitpod&logoColor=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fcore%2Fmacro_tools%2Fexamples%2Fmacro_tools_trivial.rs,RUN_POSTFIX=--example%20macro_tools_trivial/https://github.com/Wandalen/wTools) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) Tools for writing procedural macros. -### Basic use-case +### Example: Trivial One +The purpose of `typ::type_parameters` is to extract type parameters from a given Rust type. +In this example, we generate a type `core::option::Option` and extract its type parameters. + ```rust +#[ cfg( not( feature = "enabled" ) ) ] +fn main(){} #[ cfg( feature = "enabled" ) ] +fn main() { - use macro_tools::exposed::*; + // Import necessary macros and modules from the `macro_tools` crate. + use macro_tools::{ typ, qt }; + // Generate a token stream representing the type `core::option::Option`. let code = qt!( core::option::Option< i8, i16, i32, i64 > ); + + // Parse the generated token stream into a `syn::Type` object. + // `syn::Type` is a syntax tree node representing a Rust type. let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); + + // Extract type parameters from the parsed type. + // `typ::type_parameters` takes a reference to a `syn::Type` and a range. + // It returns a vector of type parameters within the specified range. + // Here, `0..=2` specifies that we are interested in the first three type parameters. let got = typ::type_parameters( &tree_type, 0..=2 ); + + // Iterate over the extracted type parameters and print each one. + // The `qt!` macro is used to convert the type parameter back to a token stream for printing. got.iter().for_each( | e | println!( "{}", qt!( #e ) ) ); - /* print : - i8 - i16 - i32 + + /* Expected output: + i8 + i16 + i32 */ } ``` +Try out `cargo run --example macro_tools_trivial`. +
+[See code](./examples/macro_tools_trivial.rs). + +### Example: Attribute Properties + +This example demonstrates an approach to parsing attributes and their properties. +The attributes are collected into a struct that aggregates them, and attribute properties +are parsed using reusable components from a library. The example shows how to use +`AttributePropertyBoolean` for parsing boolean properties and the roles of the traits +`AttributePropertyComponent` and `AttributeComponent`. The `Assign` trait is +also used to simplify the logic of assigning fields. + +Attributes are collected into a `ItemAttributes` struct, and attribute properties are parsed +using reusable components like `AttributePropertyBoolean`. + +- `AttributeComponent`: A trait that defines how an attribute should be parsed from a `syn::Attribute`. +- `AttributePropertyComponent`: A trait that defines a marker for attribute properties. +- `Assign`: A trait that simplifies the logic of assigning fields to a struct. Using a +component-based approach requires each field to have a unique type, which aligns with the +strengths of strongly-typed languages. This method ensures that the logic of +assigning values to fields is encapsulated within the fields themselves, promoting modularity +and reusability. + +The reusable property components from the library come with parameters that distinguish +different properties of the same type. This is useful when an attribute has multiple boolean +properties, for instance. Such an approach helps to avoid limitations where it is +always possible to define traits for custom types, while it may not be possible for types +defined in other crates. + +```rust + +#[ cfg( not( all( feature = "enabled", debug_assertions ) ) ) ] +fn main(){} +#[ cfg( all( feature = "enabled", debug_assertions ) ) ] +fn main() +{ + + use macro_tools:: + { + attr, + syn_err, + return_syn_err, + qt, + Result, + AttributeComponent, + AttributePropertyComponent, + AttributePropertyBoolean, + AttributePropertySingletone, + }; + use former_types::Assign; + + /// Represents the attributes of a struct. Aggregates all its attributes. + #[ derive( Debug, Default ) ] + pub struct ItemAttributes + { + /// Attribute for customizing the mutation process. + pub mutator : AttributeMutator, + } + + impl ItemAttributes + { + /// Constructs a `ItemAttributes` instance from an iterator of attributes. + /// + /// This function parses the provided attributes and assigns them to the + /// appropriate fields in the `ItemAttributes` struct. + pub fn from_attrs< 'a >( attrs : impl Iterator< Item = & 'a syn::Attribute > ) -> Result< Self > + { + let mut result = Self::default(); + + // Closure to generate an error message for unknown attributes. + let error = | attr : & syn::Attribute | -> syn::Error + { + let known_attributes = const_format::concatcp! + ( + "Known attributes are: ", + "debug", + ", ", AttributeMutator::KEYWORD, + "." + ); + syn_err! + ( + attr, + "Expects an attribute of format '#[ attribute( key1 = val1, key2 = val2 ) ]'\n {known_attributes}\n But got: '{}'", + qt! { #attr } + ) + }; + + for attr in attrs + { + let key_ident = attr.path().get_ident().ok_or_else( || error( attr ) )?; + let key_str = format!( "{}", key_ident ); + match key_str.as_ref() + { + AttributeMutator::KEYWORD => result.assign( AttributeMutator::from_meta( attr )? ), + "debug" => {}, + _ => {}, + } + } + + Ok( result ) + } + } + + /// Represents attributes for customizing the mutation process in a forming operation. + /// + /// ## Example of code + /// + /// ```ignore + /// #[ mutator( custom = true, debug = 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 : AttributePropertyCustom, + /// Specifies whether to print code generated for the field. + /// Defaults to `false`, which means no hint is provided unless explicitly requested. + pub debug : AttributePropertyDebug, + } + + impl AttributeComponent for AttributeMutator + { + const KEYWORD : & 'static str = "mutator"; + + /// Parses a `syn::Attribute` into an `AttributeMutator`. + fn from_meta( attr : & syn::Attribute ) -> Result< Self > + { + match attr.meta + { + syn::Meta::List( ref meta_list ) => + { + return syn::parse2::< AttributeMutator >( meta_list.tokens.clone() ); + }, + syn::Meta::Path( ref _path ) => + { + return Ok( Default::default() ) + }, + _ => return_syn_err! + ( + attr, + "Expects an attribute of format `#[ mutator( custom = true ) ]`. \nGot: {}", + qt! { #attr } + ), + } + } + } + + // Implement `Assign` trait to allow assigning `AttributeMutator` to `ItemAttributes`. + impl< IntoT > Assign< AttributeMutator, IntoT > for ItemAttributes + where + IntoT : Into< AttributeMutator >, + { + #[ inline( always ) ] + fn assign( & mut self, component : IntoT ) + { + self.mutator = component.into(); + } + } + + // Implement `Assign` trait to allow assigning `AttributePropertyDebug` to `AttributeMutator`. + impl< IntoT > Assign< AttributePropertyDebug, IntoT > for AttributeMutator + where + IntoT : Into< AttributePropertyDebug >, + { + #[ inline( always ) ] + fn assign( & mut self, component : IntoT ) + { + self.debug = component.into(); + } + } + + // Implement `Assign` trait to allow assigning `AttributePropertyCustom` to `AttributeMutator`. + impl< IntoT > Assign< AttributePropertyCustom, IntoT > for AttributeMutator + where + IntoT : Into< AttributePropertyCustom >, + { + #[ inline( always ) ] + fn assign( & mut self, component : IntoT ) + { + self.custom = component.into(); + } + } + + impl syn::parse::Parse for AttributeMutator + { + fn parse( input : syn::parse::ParseStream< '_ > ) -> syn::Result< Self > + { + let mut result = Self::default(); + + let error = | ident : & syn::Ident | -> syn::Error + { + let known = const_format::concatcp! + ( + "Known entries of attribute ", AttributeMutator::KEYWORD, " are: ", + AttributePropertyCustom::KEYWORD, + ", ", AttributePropertyDebug::KEYWORD, + "." + ); + syn_err! + ( + ident, + r#"Expects an attribute of format '#[ mutator( custom = false ) ]' + {known} + But got: '{}' + "#, + qt! { #ident } + ) + }; + + while !input.is_empty() + { + let lookahead = input.lookahead1(); + if lookahead.peek( syn::Ident ) + { + let ident : syn::Ident = input.parse()?; + + match ident.to_string().as_str() + { + AttributePropertyCustom::KEYWORD => result.assign( AttributePropertyCustom::parse( input )? ), + AttributePropertyDebug::KEYWORD => result.assign( AttributePropertyDebug::from( true ) ), + _ => return Err( error( & ident ) ), + } + } + else + { + return Err( lookahead.error() ); + } + + // Optional comma handling + if input.peek( syn::Token![,] ) + { + input.parse::< syn::Token![,] >()?; + } + } + + Ok( result ) + } + } + + // == Attribute properties + + /// Marker type for attribute property to specify whether to provide a sketch as a hint. + /// Defaults to `false`, which means no hint is provided unless explicitly requested. + #[ derive( Debug, Default, Clone, Copy ) ] + pub struct AttributePropertyDebugMarker; + + impl AttributePropertyComponent for AttributePropertyDebugMarker + { + const KEYWORD : & 'static str = "debug"; + } + + /// Specifies whether to provide a sketch as a hint. + /// Defaults to `false`, which means no hint is provided unless explicitly requested. + pub type AttributePropertyDebug = AttributePropertySingletone< AttributePropertyDebugMarker >; + + // == + + /// Marker type for attribute property to indicate whether a custom code should be generated. + /// Defaults to `false`, meaning no custom code is generated unless explicitly requested. + #[ derive( Debug, Default, Clone, Copy ) ] + pub struct AttributePropertyCustomMarker; + + impl AttributePropertyComponent for AttributePropertyCustomMarker + { + const KEYWORD : & 'static str = "custom"; + } + + /// Indicates whether a custom code should be generated. + /// Defaults to `false`, meaning no custom code is generated unless explicitly requested. + pub type AttributePropertyCustom = AttributePropertyBoolean< AttributePropertyCustomMarker >; + + // == test code + + // Parse an attribute and construct a `ItemAttributes` instance. + let input : syn::Attribute = syn::parse_quote!( #[ mutator( custom = true ) ] ); + let attrs : ItemAttributes = ItemAttributes::from_attrs( std::iter::once( & input ) ).unwrap(); + println!( "{:?}", attrs ); + + // Test `AttributePropertyBoolean` functionality. + let attr : AttributePropertyBoolean< AttributePropertyDebugMarker > = AttributePropertyBoolean::default(); + assert_eq!( attr.internal(), false ); + let attr : AttributePropertyBoolean< AttributePropertyDebugMarker > = true.into(); + assert_eq!( attr.internal(), true ); + let attr : AttributePropertyBoolean< AttributePropertyDebugMarker > = false.into(); + assert_eq!( attr.internal(), false ); + +} + +``` + +Try out `cargo run --example macro_tools_attr_prop`. +
+[See code](./examples/macro_tools_attr_prop.rs). + ### To add to your project ```sh @@ -42,4 +358,3 @@ cd wTools cd examples/macro_tools_trivial cargo run ``` - diff --git a/module/core/macro_tools/examples/macro_tools_attr_prop.rs b/module/core/macro_tools/examples/macro_tools_attr_prop.rs new file mode 100644 index 0000000000..4625e15154 --- /dev/null +++ b/module/core/macro_tools/examples/macro_tools_attr_prop.rs @@ -0,0 +1,290 @@ +//! +//! ### Example: Attribute Properties +//! +//! This example demonstrates an approach to parsing attributes and their properties. +//! The attributes are collected into a struct that aggregates them, and attribute properties +//! are parsed using reusable components from a library. The example shows how to use +//! `AttributePropertyBoolean` for parsing boolean properties and the roles of the traits +//! `AttributePropertyComponent` and `AttributeComponent`. The `Assign` trait is +//! also used to simplify the logic of assigning fields. +//! +//! Attributes are collected into a `ItemAttributes` struct, and attribute properties are parsed +//! using reusable components like `AttributePropertyBoolean`. +//! +//! - `AttributeComponent`: A trait that defines how an attribute should be parsed from a `syn::Attribute`. +//! - `AttributePropertyComponent`: A trait that defines a marker for attribute properties. +//! - `Assign`: A trait that simplifies the logic of assigning fields to a struct. Using a +//! component-based approach requires each field to have a unique type, which aligns with the +//! strengths of strongly-typed languages. This method ensures that the logic of +//! assigning values to fields is encapsulated within the fields themselves, promoting modularity +//! and reusability. +//! +//! The reusable property components from the library come with parameters that distinguish +//! different properties of the same type. This is useful when an attribute has multiple boolean +//! properties, for instance. Such an approach helps to avoid limitations where it is +//! always possible to define traits for custom types, while it may not be possible for types +//! defined in other crates. +//! + +#[ cfg( not( all( feature = "enabled", debug_assertions ) ) ) ] +fn main(){} +#[ cfg( all( feature = "enabled", debug_assertions ) ) ] +fn main() +{ + + use macro_tools:: + { + attr, + syn_err, + return_syn_err, + qt, + Result, + AttributeComponent, + AttributePropertyComponent, + AttributePropertyBoolean, + AttributePropertySingletone, + }; + use former_types::Assign; + + /// Represents the attributes of a struct. Aggregates all its attributes. + #[ derive( Debug, Default ) ] + pub struct ItemAttributes + { + /// Attribute for customizing the mutation process. + pub mutator : AttributeMutator, + } + + impl ItemAttributes + { + /// Constructs a `ItemAttributes` instance from an iterator of attributes. + /// + /// This function parses the provided attributes and assigns them to the + /// appropriate fields in the `ItemAttributes` struct. + pub fn from_attrs< 'a >( attrs : impl Iterator< Item = & 'a syn::Attribute > ) -> Result< Self > + { + let mut result = Self::default(); + + // Closure to generate an error message for unknown attributes. + let error = | attr : & syn::Attribute | -> syn::Error + { + let known_attributes = const_format::concatcp! + ( + "Known attributes are: ", + "debug", + ", ", AttributeMutator::KEYWORD, + "." + ); + syn_err! + ( + attr, + "Expects an attribute of format '#[ attribute( key1 = val1, key2 = val2 ) ]'\n {known_attributes}\n But got: '{}'", + qt! { #attr } + ) + }; + + for attr in attrs + { + let key_ident = attr.path().get_ident().ok_or_else( || error( attr ) )?; + let key_str = format!( "{}", key_ident ); + // if attr::is_standard( & key_str ) + // { + // continue; + // } + match key_str.as_ref() + { + AttributeMutator::KEYWORD => result.assign( AttributeMutator::from_meta( attr )? ), + "debug" => {}, + _ => {}, + // _ => return Err( error( attr ) ), + } + } + + Ok( result ) + } + } + + /// Represents attributes for customizing the mutation process in a forming operation. + /// + /// ## Example of code + /// + /// ```ignore + /// #[ mutator( custom = true, debug = 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 : AttributePropertyCustom, + /// Specifies whether to print code generated for the field. + /// Defaults to `false`, which means no hint is provided unless explicitly requested. + pub debug : AttributePropertyDebug, + } + + impl AttributeComponent for AttributeMutator + { + const KEYWORD : & 'static str = "mutator"; + + /// Parses a `syn::Attribute` into an `AttributeMutator`. + fn from_meta( attr : & syn::Attribute ) -> Result< Self > + { + match attr.meta + { + syn::Meta::List( ref meta_list ) => + { + return syn::parse2::< AttributeMutator >( meta_list.tokens.clone() ); + }, + syn::Meta::Path( ref _path ) => + { + return Ok( Default::default() ) + }, + _ => return_syn_err! + ( + attr, + "Expects an attribute of format `#[ mutator( custom = true ) ]`. \nGot: {}", + qt! { #attr } + ), + } + } + } + + // Implement `Assign` trait to allow assigning `AttributeMutator` to `ItemAttributes`. + impl< IntoT > Assign< AttributeMutator, IntoT > for ItemAttributes + where + IntoT : Into< AttributeMutator >, + { + #[ inline( always ) ] + fn assign( & mut self, component : IntoT ) + { + self.mutator = component.into(); + } + } + + // Implement `Assign` trait to allow assigning `AttributePropertyDebug` to `AttributeMutator`. + impl< IntoT > Assign< AttributePropertyDebug, IntoT > for AttributeMutator + where + IntoT : Into< AttributePropertyDebug >, + { + #[ inline( always ) ] + fn assign( & mut self, component : IntoT ) + { + self.debug = component.into(); + } + } + + // Implement `Assign` trait to allow assigning `AttributePropertyCustom` to `AttributeMutator`. + impl< IntoT > Assign< AttributePropertyCustom, IntoT > for AttributeMutator + where + IntoT : Into< AttributePropertyCustom >, + { + #[ inline( always ) ] + fn assign( & mut self, component : IntoT ) + { + self.custom = component.into(); + } + } + + impl syn::parse::Parse for AttributeMutator + { + fn parse( input : syn::parse::ParseStream< '_ > ) -> syn::Result< Self > + { + let mut result = Self::default(); + + let error = | ident : & syn::Ident | -> syn::Error + { + let known = const_format::concatcp! + ( + "Known entries of attribute ", AttributeMutator::KEYWORD, " are: ", + AttributePropertyCustom::KEYWORD, + ", ", AttributePropertyDebug::KEYWORD, + "." + ); + syn_err! + ( + ident, + r#"Expects an attribute of format '#[ mutator( custom = false ) ]' + {known} + But got: '{}' + "#, + qt! { #ident } + ) + }; + + while !input.is_empty() + { + let lookahead = input.lookahead1(); + if lookahead.peek( syn::Ident ) + { + let ident : syn::Ident = input.parse()?; + + match ident.to_string().as_str() + { + AttributePropertyCustom::KEYWORD => result.assign( AttributePropertyCustom::parse( input )? ), + AttributePropertyDebug::KEYWORD => result.assign( AttributePropertyDebug::from( true ) ), + _ => return Err( error( & ident ) ), + } + } + else + { + return Err( lookahead.error() ); + } + + // Optional comma handling + if input.peek( syn::Token![,] ) + { + input.parse::< syn::Token![,] >()?; + } + } + + Ok( result ) + } + } + + // == Attribute properties + + /// Marker type for attribute property to specify whether to provide a sketch as a hint. + /// Defaults to `false`, which means no hint is provided unless explicitly requested. + #[ derive( Debug, Default, Clone, Copy ) ] + pub struct AttributePropertyDebugMarker; + + impl AttributePropertyComponent for AttributePropertyDebugMarker + { + const KEYWORD : & 'static str = "debug"; + } + + /// Specifies whether to provide a sketch as a hint. + /// Defaults to `false`, which means no hint is provided unless explicitly requested. + pub type AttributePropertyDebug = AttributePropertySingletone< AttributePropertyDebugMarker >; + + // == + + /// Marker type for attribute property to indicate whether a custom code should be generated. + /// Defaults to `false`, meaning no custom code is generated unless explicitly requested. + #[ derive( Debug, Default, Clone, Copy ) ] + pub struct AttributePropertyCustomMarker; + + impl AttributePropertyComponent for AttributePropertyCustomMarker + { + const KEYWORD : & 'static str = "custom"; + } + + /// Indicates whether a custom code should be generated. + /// Defaults to `false`, meaning no custom code is generated unless explicitly requested. + pub type AttributePropertyCustom = AttributePropertyBoolean< AttributePropertyCustomMarker >; + + // == test code + + // Parse an attribute and construct a `ItemAttributes` instance. + let input : syn::Attribute = syn::parse_quote!( #[ mutator( custom = true ) ] ); + let attrs : ItemAttributes = ItemAttributes::from_attrs( std::iter::once( & input ) ).unwrap(); + println!( "{:?}", attrs ); + + // Test `AttributePropertyBoolean` functionality. + let attr : AttributePropertyBoolean< AttributePropertyDebugMarker > = AttributePropertyBoolean::default(); + assert_eq!( attr.internal(), false ); + let attr : AttributePropertyBoolean< AttributePropertyDebugMarker > = true.into(); + assert_eq!( attr.internal(), true ); + let attr : AttributePropertyBoolean< AttributePropertyDebugMarker > = false.into(); + assert_eq!( attr.internal(), false ); + +} diff --git a/module/core/macro_tools/examples/macro_tools_trivial.rs b/module/core/macro_tools/examples/macro_tools_trivial.rs index 73cd1af6c8..e92559b193 100644 --- a/module/core/macro_tools/examples/macro_tools_trivial.rs +++ b/module/core/macro_tools/examples/macro_tools_trivial.rs @@ -1,19 +1,39 @@ -//! qqq : write proper description +//! This example demonstrates the use of `typ::type_parameters` from the `macro_tools` crate. +//! +//! ### Example: Trivial One +//! +//! The purpose of `typ::type_parameters` is to extract type parameters from a given Rust type. +//! In this example, we generate a type `core::option::Option` and extract its type parameters. +//! + #[ cfg( not( feature = "enabled" ) ) ] fn main(){} - #[ cfg( feature = "enabled" ) ] fn main() { + // Import necessary macros and modules from the `macro_tools` crate. use macro_tools::{ typ, qt }; + // Generate a token stream representing the type `core::option::Option`. let code = qt!( core::option::Option< i8, i16, i32, i64 > ); + + // Parse the generated token stream into a `syn::Type` object. + // `syn::Type` is a syntax tree node representing a Rust type. let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); + + // Extract type parameters from the parsed type. + // `typ::type_parameters` takes a reference to a `syn::Type` and a range. + // It returns a vector of type parameters within the specified range. + // Here, `0..=2` specifies that we are interested in the first three type parameters. let got = typ::type_parameters( &tree_type, 0..=2 ); + + // Iterate over the extracted type parameters and print each one. + // The `qt!` macro is used to convert the type parameter back to a token stream for printing. got.iter().for_each( | e | println!( "{}", qt!( #e ) ) ); - /* print : - i8 - i16 - i32 + + /* Expected output: + i8 + i16 + i32 */ -} \ No newline at end of file +} diff --git a/module/core/macro_tools/src/attr.rs b/module/core/macro_tools/src/attr.rs index c3a3cc2707..4524ded53e 100644 --- a/module/core/macro_tools/src/attr.rs +++ b/module/core/macro_tools/src/attr.rs @@ -5,34 +5,7 @@ /// Internal namespace. pub( crate ) mod private { - use super::super::*; - - /// - /// For attribute like `#[former( default = 31 ) ]` return key `default` and value `31`, - /// as well as syn::Meta as the last element of result tuple. - /// - /// ### Basic use-case. - /// ```rust - /// use macro_tools::exposed::*; - /// let attr : syn::Attribute = syn::parse_quote!( #[ former( default = 31 ) ] ); - /// // tree_print!( attr ); - /// let got = equation( &attr ).unwrap(); - /// assert_eq!( code_to_str!( got ), "default = 31".to_string() ); - /// ``` - - pub fn equation( attr : &syn::Attribute ) -> Result< tokens::Equation > - { - let meta = &attr.meta; - return match meta - { - syn::Meta::List( ref meta_list ) => - { - let eq : tokens::Equation = syn::parse2( meta_list.tokens.clone() )?; - Ok( eq ) - } - _ => return Err( syn::Error::new( attr.span(), "Unknown format of attribute, expected syn::Meta::List( meta_list )" ) ), - }; - } + use crate::*; /// Checks if the given iterator of attributes contains an attribute named `debug`. /// @@ -274,25 +247,14 @@ pub( crate ) mod private } } - // - - // types! - // { - - /// - /// Attribute which is outer. - /// - /// For example: `#[ derive( Copy ) ]`. - /// - - // #[ derive( Debug, PartialEq, Eq, Clone, Default ) ] - // pub many AttributesOuter : syn::Attribute; - // xxx : apply maybe collection of derives for TDD - - #[ derive( Debug, PartialEq, Eq, Clone, Default ) ] - pub struct AttributesOuter( pub Vec< syn::Attribute > ); - - // } + /// Represents a collection of outer attributes. + /// + /// This struct wraps a `Vec< syn::Attribute >`, providing utility methods for parsing, + /// converting, and iterating over outer attributes. Outer attributes are those that + /// appear outside of an item, such as `#[ ... ]` annotations in Rust. + /// + #[ derive( Debug, PartialEq, Eq, Clone, Default ) ] + pub struct AttributesOuter( pub Vec< syn::Attribute > ); impl From< Vec< syn::Attribute > > for AttributesOuter { @@ -359,28 +321,105 @@ pub( crate ) mod private } } + /// Trait for components of a structure aggregating attributes that can be constructed from a meta attribute. /// - /// Attribute and ident. + /// The `AttributeComponent` trait defines the interface for components that can be created + /// from a `syn::Attribute` meta item. Implementors of this trait are required to define + /// a constant `KEYWORD` that identifies the type of the component and a method `from_meta` + /// that handles the construction of the component from the given attribute. /// - - // qqq : example? - - pub type AttributedIdent = Pair< Many< AttributesInner >, syn::Ident >; - - impl From< syn::Ident > for AttributedIdent + /// This trait is designed to facilitate modular and reusable parsing of attributes applied + /// to structs, enums, or other constructs. By implementing this trait, you can create specific + /// components from attributes and then aggregate these components into a larger structure. + /// + /// # Example + /// + /// ```rust + /// use macro_tools::{ AttributeComponent, Result }; + /// use syn::{ Attribute, Error }; + /// + /// struct MyComponent; + /// + /// impl AttributeComponent for MyComponent + /// { + /// const KEYWORD : &'static str = "my_component"; + /// + /// fn from_meta( attr : &Attribute ) -> Result + /// { + /// // Parsing logic here + /// // Return Ok(MyComponent) if parsing is successful + /// // Return Err(Error::new_spanned(attr, "error message")) if parsing fails + /// Ok( MyComponent ) + /// } + /// } + /// ``` + /// + /// # Parameters + /// + /// - `attr` : A reference to the `syn::Attribute` from which the component is to be constructed. + /// + /// # Returns + /// + /// A `Result` containing the constructed component if successful, or an error if the parsing fails. + /// + pub trait AttributeComponent + where + Self : Sized, { - fn from( src : syn::Ident ) -> Self - { - Self( Vec::< AttributesInner >::new().into(), src ) - } + /// The keyword that identifies the component. + /// + /// This constant is used to match the attribute to the corresponding component. + /// Each implementor of this trait must provide a unique keyword for its type. + const KEYWORD : &'static str; + + /// Constructs the component from the given meta attribute. + /// + /// This method is responsible for parsing the provided `syn::Attribute` and + /// returning an instance of the component. If the attribute cannot be parsed + /// into the component, an error should be returned. + /// + /// # Parameters + /// + /// - `attr` : A reference to the `syn::Attribute` from which the component is to be constructed. + /// + /// # Returns + /// + /// A `Result` containing the constructed component if successful, or an error if the parsing fails. + fn from_meta( attr : &syn::Attribute ) -> Result< Self >; } - impl From< AttributedIdent > for syn::Ident + /// Trait for properties of an attribute component that can be identified by a keyword. + /// + /// The `AttributePropertyComponent` trait defines the interface for attribute properties + /// that can be identified by a specific keyword. Implementors of this trait are required + /// to define a constant `KEYWORD` that identifies the type of the property. + /// + /// This trait is useful in scenarios where attributes may have multiple properties + /// that need to be parsed and handled separately. By defining a unique keyword for each property, + /// the parsing logic can accurately identify and process each property. + /// + /// # Example + /// + /// ```rust + /// use macro_tools::AttributePropertyComponent; + /// + /// struct MyProperty; + /// + /// impl AttributePropertyComponent for MyProperty + /// { + /// const KEYWORD : &'static str = "my_property"; + /// } + /// ``` + /// + pub trait AttributePropertyComponent + where + Self : Sized, { - fn from( src : AttributedIdent ) -> Self - { - src.1 - } + /// The keyword that identifies the component. + /// + /// This constant is used to match the attribute to the corresponding property. + /// Each implementor of this trait must provide a unique keyword for its type. + const KEYWORD : &'static str; } } @@ -395,6 +434,14 @@ pub mod protected #[ doc( inline ) ] #[ allow( unused_imports ) ] pub use super::orphan::*; + #[ doc( inline ) ] + #[ allow( unused_imports ) ] + pub use super::private:: + { + // equation, + has_debug, + is_standard, + }; } /// Orphan namespace of the module. @@ -416,12 +463,11 @@ pub mod exposed #[ allow( unused_imports ) ] pub use super::private:: { - equation, - has_debug, - is_standard, AttributesInner, AttributesOuter, - AttributedIdent, + + AttributeComponent, + AttributePropertyComponent, }; } diff --git a/module/core/macro_tools/src/attr_prop.rs b/module/core/macro_tools/src/attr_prop.rs new file mode 100644 index 0000000000..4e5020eff2 --- /dev/null +++ b/module/core/macro_tools/src/attr_prop.rs @@ -0,0 +1,168 @@ +//! +//! Attribute's properties. Reuse them to define how to parse properties of an attribute. +//! +//! # Example +//! +//! ```rust +//! use macro_tools::AttributePropertyBoolean; +//! +//! #[ derive( Debug, Default, Clone, Copy ) ] +//! pub struct DebugMarker; +//! +//! #[ derive( Debug, Default, Clone, Copy ) ] +//! pub struct EnabledMarker; +//! +//! pub trait AttributePropertyComponent +//! { +//! const KEYWORD : &'static str; +//! } +//! +//! impl AttributePropertyComponent for DebugMarker +//! { +//! const KEYWORD : &'static str = "debug"; +//! } +//! +//! impl AttributePropertyComponent for EnabledMarker +//! { +//! const KEYWORD : &'static str = "enabled"; +//! } +//! +//! #[ derive( Debug, Default ) ] +//! struct MyAttributes +//! { +//! pub debug : AttributePropertyBoolean< DebugMarker >, +//! pub enabled : AttributePropertyBoolean< EnabledMarker >, +//! } +//! +//! impl syn::parse::Parse for MyAttributes +//! { +//! fn parse( input : syn::parse::ParseStream< '_ > ) -> syn::Result< Self > +//! { +//! let mut debug = AttributePropertyBoolean::< DebugMarker >::default(); +//! let mut enabled = AttributePropertyBoolean::< EnabledMarker >::default(); +//! +//! while !input.is_empty() +//! { +//! let lookahead = input.lookahead1(); +//! if lookahead.peek( syn::Ident ) +//! { +//! let ident : syn::Ident = input.parse()?; +//! match ident.to_string().as_str() +//! { +//! DebugMarker::KEYWORD => debug = input.parse()?, +//! EnabledMarker::KEYWORD => enabled = input.parse()?, +//! _ => return Err( lookahead.error() ), +//! } +//! } +//! else +//! { +//! return Err( lookahead.error() ); +//! } +//! +//! // Optional comma handling +//! if input.peek( syn::Token![,] ) +//! { +//! input.parse::< syn::Token![,] >()?; +//! } +//! } +//! +//! Ok( MyAttributes { debug, enabled } ) +//! } +//! } +//! +//! let input : syn::Attribute = syn::parse_quote!( #[ attribute( enabled = true ) ] ); +//! let meta = match input.meta +//! { +//! syn::Meta::List( meta_list ) => meta_list, +//! _ => panic!( "Expected a Meta::List" ), +//! }; +//! +//! let nested_meta_stream : proc_macro2::TokenStream = meta.tokens; +//! let attrs : MyAttributes = syn::parse2( nested_meta_stream ).unwrap(); +//! println!( "{:?}", attrs ); +//! ``` +//! +//! In this example, the `AttributePropertyBoolean` struct is used to define attributes with boolean properties. +//! The `DebugMarker` and `EnabledMarker` structs act as markers to distinguish between different boolean attributes. +//! The `MyAttributes` struct aggregates these boolean attributes. +//! +//! The `Parse` implementation for `MyAttributes` iterates through the attribute's key-value pairs, +//! identifying each by its marker's keyword and parsing the boolean value. +//! It uses the `ParseStream` to parse identifiers and their associated values, +//! matching them to the appropriate marker's keyword. +//! If an unrecognized identifier is encountered, it returns an error. +//! +//! The `parse_quote!` macro is used to create a `syn::Attribute` instance with the attribute syntax, +//! which is then parsed into the `MyAttributes` struct. The resulting `MyAttributes` instance is printed to the console. + +mod singletone; +mod singletone_optional; +mod boolean; +mod boolean_optional; +mod syn; +mod syn_optional; + +/// Internal namespace. +pub( crate ) mod private +{ + // use crate::*; + +} + +#[ doc( inline ) ] +#[ allow( unused_imports ) ] +pub use protected::*; + +/// Protected namespace of the module. +pub mod protected +{ + #[ doc( inline ) ] + #[ allow( unused_imports ) ] + pub use super::orphan::*; + #[ doc( inline ) ] + #[ allow( unused_imports ) ] + pub use super::private:: + { + }; +} + +/// Orphan namespace of the module. +pub mod orphan +{ + #[ doc( inline ) ] + #[ allow( unused_imports ) ] + pub use super::exposed::*; +} + +/// Exposed namespace of the module. +pub mod exposed +{ + pub use super::protected as attr_prop; + #[ doc( inline ) ] + #[ allow( unused_imports ) ] + pub use super::prelude::*; + #[ doc( inline ) ] + #[ allow( unused_imports ) ] + pub use super:: + { + + singletone::AttributePropertySingletone, + singletone::AttributePropertySingletoneMarker, + singletone_optional::AttributePropertyOptionalSingletone, + singletone_optional::AttributePropertyOptionalSingletoneMarker, + boolean::AttributePropertyBoolean, + boolean::AttributePropertyBooleanMarker, + boolean_optional::AttributePropertyOptionalBoolean, + boolean_optional::AttributePropertyOptionalBooleanMarker, + syn::AttributePropertySyn, + syn::AttributePropertySynMarker, + syn_optional::AttributePropertyOptionalSyn, + syn_optional::AttributePropertyOptionalSynMarker, + + }; +} + +/// Prelude to use essentials: `use my_module::prelude::*`. +pub mod prelude +{ +} diff --git a/module/core/macro_tools/src/attr_prop/boolean.rs b/module/core/macro_tools/src/attr_prop/boolean.rs new file mode 100644 index 0000000000..4472b3ae42 --- /dev/null +++ b/module/core/macro_tools/src/attr_prop/boolean.rs @@ -0,0 +1,196 @@ +//! +//! A generic boolean attribute property. +//! Defaults to `false`. +//! + +use crate::*; +use former_types::Assign; + +/// Default marker for `AttributePropertyBoolean`. +/// Used if no marker is defined as parameter. +#[ derive( Debug, Default, Clone, Copy ) ] +pub struct AttributePropertyBooleanMarker; + +/// A generic boolean attribute property. +/// Defaults to `false`. +/// +/// # Example +/// +/// ```rust +/// use macro_tools::AttributePropertyBoolean; +/// +/// #[ derive( Debug, Default, Clone, Copy ) ] +/// pub struct DebugMarker; +/// +/// #[ derive( Debug, Default, Clone, Copy ) ] +/// pub struct EnabledMarker; +/// +/// pub trait AttributePropertyComponent +/// { +/// const KEYWORD : &'static str; +/// } +/// +/// impl AttributePropertyComponent for DebugMarker +/// { +/// const KEYWORD : &'static str = "debug"; +/// } +/// +/// impl AttributePropertyComponent for EnabledMarker +/// { +/// const KEYWORD : &'static str = "enabled"; +/// } +/// +/// #[ derive( Debug, Default ) ] +/// struct MyAttributes +/// { +/// pub debug : AttributePropertyBoolean< DebugMarker >, +/// pub enabled : AttributePropertyBoolean< EnabledMarker >, +/// } +/// +/// impl syn::parse::Parse for MyAttributes +/// { +/// fn parse( input : syn::parse::ParseStream< '_ > ) -> syn::Result< Self > +/// { +/// let mut debug = AttributePropertyBoolean::< DebugMarker >::default(); +/// let mut enabled = AttributePropertyBoolean::< EnabledMarker >::default(); +/// +/// while !input.is_empty() +/// { +/// let lookahead = input.lookahead1(); +/// if lookahead.peek( syn::Ident ) +/// { +/// let ident : syn::Ident = input.parse()?; +/// match ident.to_string().as_str() +/// { +/// DebugMarker::KEYWORD => debug = input.parse()?, +/// EnabledMarker::KEYWORD => enabled = input.parse()?, +/// _ => return Err( lookahead.error() ), +/// } +/// } +/// else +/// { +/// return Err( lookahead.error() ); +/// } +/// +/// // Optional comma handling +/// if input.peek( syn::Token![,] ) +/// { +/// input.parse::< syn::Token![,] >()?; +/// } +/// } +/// +/// Ok( MyAttributes { debug, enabled } ) +/// } +/// } +/// +/// let input : syn::Attribute = syn::parse_quote!( #[ attribute( enabled = true ) ] ); +/// let meta = match input.meta +/// { +/// syn::Meta::List( meta_list ) => meta_list, +/// _ => panic!( "Expected a Meta::List" ), +/// }; +/// +/// let nested_meta_stream : proc_macro2::TokenStream = meta.tokens; +/// let attrs : MyAttributes = syn::parse2( nested_meta_stream ).unwrap(); +/// println!( "{:?}", attrs ); +/// ``` +/// +/// In this example, the `AttributePropertyBoolean` struct is used to define attributes with boolean properties. +/// The `DebugMarker` and `EnabledMarker` structs act as markers to distinguish between different boolean attributes. +/// The `MyAttributes` struct aggregates these boolean attributes. +/// +/// The `Parse` implementation for `MyAttributes` iterates through the attribute's key-value pairs, +/// identifying each by its marker's keyword and parsing the boolean value. +/// It uses the `ParseStream` to parse identifiers and their associated values, +/// matching them to the appropriate marker's keyword. +/// If an unrecognized identifier is encountered, it returns an error. +/// +/// The `parse_quote!` macro is used to create a `syn::Attribute` instance with the attribute syntax, +/// which is then parsed into the `MyAttributes` struct. The resulting `MyAttributes` instance is printed to the console. + +#[ derive( Debug, Default, Clone, Copy ) ] +pub struct AttributePropertyBoolean< Marker = AttributePropertyBooleanMarker >( bool, ::core::marker::PhantomData< Marker > ); + +impl< Marker > AttributePropertyBoolean< Marker > +{ + /// Just unwraps and returns the internal data. + #[ inline( always ) ] + pub fn internal( self ) -> bool + { + self.0 + } + + /// Returns a reference to the internal boolean value. + #[ inline( always ) ] + pub fn ref_internal( &self ) -> &bool + { + &self.0 + } +} + +impl< Marker, IntoT > Assign< AttributePropertyBoolean< Marker >, IntoT > +for AttributePropertyBoolean< Marker > +where + IntoT : Into< AttributePropertyBoolean< Marker > >, +{ + #[ inline( always ) ] + fn assign( &mut self, component : IntoT ) + { + *self = component.into(); + } +} + +impl< Marker > AttributePropertyComponent for AttributePropertyBoolean< Marker > +where + Marker : AttributePropertyComponent, +{ + const KEYWORD : &'static str = Marker::KEYWORD; +} + +impl< Marker > syn::parse::Parse for AttributePropertyBoolean< Marker > +{ + fn parse( input : syn::parse::ParseStream< '_ > ) -> syn::Result< Self > + { + input.parse::< syn::Token![ = ] >()?; + let value : syn::LitBool = input.parse()?; + Ok( value.value.into() ) + } +} + +impl< Marker > From< bool > for AttributePropertyBoolean< Marker > +{ + #[ inline( always ) ] + fn from( src : bool ) -> Self + { + Self( src, Default::default() ) + } +} + +impl< Marker > From< AttributePropertyBoolean< Marker > > for bool +{ + #[ inline( always ) ] + fn from( src : AttributePropertyBoolean< Marker > ) -> Self + { + src.0 + } +} + +impl< Marker > core::ops::Deref for AttributePropertyBoolean< Marker > +{ + type Target = bool; + + #[ inline( always ) ] + fn deref( &self ) -> &bool + { + &self.0 + } +} + +impl< Marker > AsRef< bool > for AttributePropertyBoolean< Marker > +{ + #[ inline( always ) ] + fn as_ref( &self ) -> &bool + { + &self.0 + } +} diff --git a/module/core/macro_tools/src/attr_prop/boolean_optional.rs b/module/core/macro_tools/src/attr_prop/boolean_optional.rs new file mode 100644 index 0000000000..680803f4c8 --- /dev/null +++ b/module/core/macro_tools/src/attr_prop/boolean_optional.rs @@ -0,0 +1,117 @@ +//! +//! A generic optional boolean attribute property: `Option< bool >`. +//! Defaults to `false`. +//! + +use crate::*; +use former_types::Assign; + +/// Default marker for `AttributePropertyOptionalSingletone`. +/// Used if no marker is defined as parameter. +#[ derive( Debug, Default, Clone, Copy ) ] +pub struct AttributePropertyOptionalBooleanMarker; + +/// A generic optional boolean attribute property: `Option< bool >`. +/// Defaults to `false`. +#[ derive( Debug, Default, Clone, Copy ) ] +pub struct AttributePropertyOptionalBoolean< Marker = AttributePropertyOptionalBooleanMarker >( Option< bool >, ::core::marker::PhantomData< Marker > ); + +impl< Marker > AttributePropertyOptionalBoolean< Marker > +{ + /// Just unwraps and returns the internal data. + #[ inline( always ) ] + pub fn internal( self ) -> Option< bool > + { + self.0 + } + + /// Returns a reference to the internal optional boolean value. + #[ inline( always ) ] + pub fn ref_internal( &self ) -> Option< &bool > + { + self.0.as_ref() + } + +} + +impl< Marker, IntoT > Assign< AttributePropertyOptionalBoolean< Marker >, IntoT > +for AttributePropertyOptionalBoolean< Marker > +where + IntoT : Into< AttributePropertyOptionalBoolean< Marker > >, +{ + /// Inserts value of another instance into the option if it is None, then returns a mutable reference to the contained value. + /// If another instance does is None then do nothing. + #[ inline( always ) ] + fn assign( &mut self, component : IntoT ) + { + let component = component.into(); + match component.0 + { + Some( val ) => { self.0 = Some( val ); }, + None => {}, + } + } +} + +impl< Marker > AttributePropertyComponent for AttributePropertyOptionalBoolean< Marker > +where + Marker : AttributePropertyComponent, +{ + const KEYWORD : &'static str = Marker::KEYWORD; +} + +impl< Marker > syn::parse::Parse for AttributePropertyOptionalBoolean< Marker > +{ + fn parse( input : syn::parse::ParseStream< '_ > ) -> syn::Result< Self > + { + input.parse::< syn::Token![ = ] >()?; + let value : syn::LitBool = input.parse()?; + Ok( value.value.into() ) + } +} + +impl< Marker > From< bool > for AttributePropertyOptionalBoolean< Marker > +{ + #[ inline( always ) ] + fn from( src : bool ) -> Self + { + Self( Some( src ), Default::default() ) + } +} + +impl< Marker > From< Option< bool > > for AttributePropertyOptionalBoolean< Marker > +{ + #[ inline( always ) ] + fn from( src : Option< bool > ) -> Self + { + Self( src, Default::default() ) + } +} + +impl< Marker > From< AttributePropertyOptionalBoolean< Marker > > for Option< bool > +{ + #[ inline( always ) ] + fn from( src : AttributePropertyOptionalBoolean< Marker > ) -> Self + { + src.0 + } +} + +impl< Marker > core::ops::Deref for AttributePropertyOptionalBoolean< Marker > +{ + type Target = Option< bool >; + #[ inline( always ) ] + fn deref( &self ) -> &Option< bool > + { + &self.0 + } +} + +impl< Marker > AsRef< Option< bool > > for AttributePropertyOptionalBoolean< Marker > +{ + #[ inline( always ) ] + fn as_ref( &self ) -> &Option< bool > + { + &self.0 + } +} diff --git a/module/core/macro_tools/src/attr_prop/singletone.rs b/module/core/macro_tools/src/attr_prop/singletone.rs new file mode 100644 index 0000000000..1d55d9ac7c --- /dev/null +++ b/module/core/macro_tools/src/attr_prop/singletone.rs @@ -0,0 +1,108 @@ +//! A generic `bool` attribute property which consists of only keyword. +//! Defaults to `None`. +//! +//! This property can have two states: `true`, or `false`. +//! +//! # Example +//! +//! ```ignore +//! #[ attribute( some ) ] +//! ``` +//! +//! This is useful for attributes that need to enable or disable features or flags. + +use crate::*; +use former_types::Assign; + +/// Default marker for `AttributePropertySingletone`. +/// Used if no marker is defined as parameter. +#[ derive( Debug, Default, Clone, Copy ) ] +pub struct AttributePropertySingletoneMarker; + +/// A generic boolean attribute property which consists of only keyword. +/// This property can have two states: `true`, or `false`. +/// Defaults to `false`. +/// +/// Unlike other properties, it does not implement parse, because it consists only of keyword which should be parsed outside of the property. +#[ derive( Debug, Default, Clone, Copy ) ] +pub struct AttributePropertySingletone< Marker = AttributePropertySingletoneMarker > +( + bool, + ::core::marker::PhantomData< Marker >, +); + +impl< Marker > AttributePropertySingletone< Marker > +{ + + /// Unwraps and returns the internal optional boolean value. + #[ inline( always ) ] + pub fn internal( self ) -> bool + { + self.0 + } + + /// Returns a reference to the internal optional boolean value. + #[ inline( always ) ] + pub fn ref_internal( &self ) -> &bool + { + &self.0 + } + +} + +impl< Marker, IntoT > Assign< AttributePropertySingletone< Marker >, IntoT > +for AttributePropertySingletone< Marker > +where + IntoT : Into< AttributePropertySingletone< Marker > >, +{ + #[ inline( always ) ] + fn assign( &mut self, component : IntoT ) + { + *self = component.into(); + } +} + +impl< Marker > AttributePropertyComponent for AttributePropertySingletone< Marker > +where + Marker : AttributePropertyComponent, +{ + const KEYWORD : &'static str = Marker::KEYWORD; +} + +impl< Marker > From< bool > for AttributePropertySingletone< Marker > +{ + #[ inline( always ) ] + fn from( src : bool ) -> Self + { + Self( src, Default::default() ) + } +} + +impl< Marker > From< AttributePropertySingletone< Marker > > for bool +{ + #[ inline( always ) ] + fn from( src : AttributePropertySingletone< Marker > ) -> Self + { + src.0 + } +} + +impl< Marker > core::ops::Deref for AttributePropertySingletone< Marker > +{ + type Target = bool; + + #[ inline( always ) ] + fn deref( &self ) -> &bool + { + &self.0 + } +} + +impl< Marker > AsRef< bool > for AttributePropertySingletone< Marker > +{ + #[ inline( always ) ] + fn as_ref( &self ) -> &bool + { + &self.0 + } +} diff --git a/module/core/macro_tools/src/attr_prop/singletone_optional.rs b/module/core/macro_tools/src/attr_prop/singletone_optional.rs new file mode 100644 index 0000000000..39c3dd9940 --- /dev/null +++ b/module/core/macro_tools/src/attr_prop/singletone_optional.rs @@ -0,0 +1,139 @@ +//! A generic `Option< bool >` attribute property which consists of only keyword. +//! Defaults to `None`. +//! +//! This property can have three states: `None`, `Some( true )`, or `Some( false )`. +//! It parses `on` and `off` keywords to represent `Some( true )` and `Some( false )` respectively. +//! +//! # Example +//! +//! ```ignore +//! #[ attribute( on) ] +//! #[ attribute( off ) ] +//! ``` +//! +//! This is useful for attributes that need to enable or disable features or flags. + +use crate::*; +use former_types::Assign; + +/// Default marker for `AttributePropertyOptionalSingletone`. +/// Used if no marker is defined as parameter. +#[ derive( Debug, Default, Clone, Copy ) ] +pub struct AttributePropertyOptionalSingletoneMarker; + +/// A generic attribute property for switching on/off. +/// Has 3 states: `None`, `Some( true )`, `Some( false )`. +/// Defaults to `None`. +/// +/// Unlike [`AttributePropertyOptionalBoolean`], it "understands" `on`, `off` keywords during parsing. +/// For example: `#[ attribute( on ) ]` and `#[ attribute( off )]`. +/// As a consequence, the property has two keywords. +#[ derive( Debug, Default, Clone, Copy ) ] +pub struct AttributePropertyOptionalSingletone< Marker = AttributePropertyOptionalSingletoneMarker > +( + Option< bool >, + ::core::marker::PhantomData< Marker >, +); + +impl< Marker > AttributePropertyOptionalSingletone< Marker > +{ + + /// Return bool value: on/off, use argument as default if it's `None`. + #[ inline ] + pub fn value( self, default : bool ) -> bool + { + if self.0.is_none() + { + return default; + } + self.0.unwrap() + } + + /// Unwraps and returns the internal optional boolean value. + #[ inline( always ) ] + pub fn internal( self ) -> Option< bool > + { + self.0 + } + + /// Returns a reference to the internal optional boolean value. + #[ inline( always ) ] + pub fn ref_internal( &self ) -> Option< &bool > + { + self.0.as_ref() + } + +} + +impl< Marker, IntoT > Assign< AttributePropertyOptionalSingletone< Marker >, IntoT > +for AttributePropertyOptionalSingletone< Marker > +where + IntoT : Into< AttributePropertyOptionalSingletone< Marker > >, +{ + /// Inserts value of another instance into the option if it is None, then returns a mutable reference to the contained value. + /// If another instance does is None then do nothing. + #[ inline( always ) ] + fn assign( &mut self, component : IntoT ) + { + let component = component.into(); + match component.0 + { + Some( val ) => { self.0 = Some( val ); }, + None => {}, + } + } +} + +impl< Marker > AttributePropertyComponent for AttributePropertyOptionalSingletone< Marker > +where + Marker : AttributePropertyComponent, +{ + const KEYWORD : &'static str = Marker::KEYWORD; +} + +impl< Marker > From< bool > for AttributePropertyOptionalSingletone< Marker > +{ + #[ inline( always ) ] + fn from( src : bool ) -> Self + { + Self( Some( src ), Default::default() ) + } +} + +impl< Marker > From< Option< bool > > for AttributePropertyOptionalSingletone< Marker > +{ + #[ inline( always ) ] + fn from( src : Option< bool > ) -> Self + { + Self( src, Default::default() ) + } +} + +impl< Marker > From< AttributePropertyOptionalSingletone< Marker > > for Option< bool > +{ + #[ inline( always ) ] + fn from( src : AttributePropertyOptionalSingletone< Marker > ) -> Self + { + src.0 + } +} + +impl< Marker > core::ops::Deref for AttributePropertyOptionalSingletone< Marker > +{ + type Target = Option< bool >; + + #[ inline( always ) ] + fn deref( &self ) -> &Option< bool > + { + &self.0 + } +} + +impl< Marker > AsRef< Option< bool > > for AttributePropertyOptionalSingletone< Marker > +{ + #[ inline( always ) ] + fn as_ref( &self ) -> &Option< bool > + { + &self.0 + } +} diff --git a/module/core/macro_tools/src/attr_prop/syn.rs b/module/core/macro_tools/src/attr_prop/syn.rs new file mode 100644 index 0000000000..c60a21cfdd --- /dev/null +++ b/module/core/macro_tools/src/attr_prop/syn.rs @@ -0,0 +1,115 @@ +//! +//! Property of an attribute which simply wraps one of the standard `syn` types. +//! + +use crate::*; +use former_types::Assign; + +/// Default marker for `AttributePropertySyn`. +/// Used if no marker is defined as parameter. +#[ derive( Debug, Default, Clone, Copy ) ] +pub struct AttributePropertySynMarker; + +/// +/// Property of an attribute which simply wraps one of the standard `syn` types. +/// + +#[ derive( Debug, Clone ) ] +pub struct AttributePropertySyn< T, Marker = AttributePropertySynMarker >( T, ::core::marker::PhantomData< Marker > ) +where + T : syn::parse::Parse + quote::ToTokens; + +impl< T, Marker > AttributePropertySyn< T, Marker > +where + T : syn::parse::Parse + quote::ToTokens, +{ + /// Just unwraps and returns the internal data. + // #[ allow( dead_code ) ] + #[ inline( always ) ] + pub fn internal( self ) -> T + { + self.0 + } + + /// Returns a reference to the internal data. + // #[ allow( dead_code ) ] + #[ inline( always ) ] + pub fn ref_internal( &self ) -> &T + { + &self.0 + } +} + +impl< T, Marker, IntoT > Assign< AttributePropertySyn< T, Marker >, IntoT > +for AttributePropertySyn< T, Marker > +where + T : syn::parse::Parse + quote::ToTokens, + IntoT : Into< AttributePropertySyn< T, Marker > >, +{ + #[ inline( always ) ] + fn assign( &mut self, component : IntoT ) + { + *self = component.into(); + } +} + +impl< T, Marker > AttributePropertyComponent for AttributePropertySyn< T, Marker > +where + T : syn::parse::Parse + quote::ToTokens, + Marker : AttributePropertyComponent, +{ + const KEYWORD : &'static str = Marker::KEYWORD; +} + +impl< T, Marker > syn::parse::Parse for AttributePropertySyn< T, Marker > +where + T : syn::parse::Parse + quote::ToTokens, +{ + fn parse( input : syn::parse::ParseStream< '_ > ) -> syn::Result< Self > + { + input.parse::< syn::Token![ = ] >()?; + let value : T = input.parse()?; + Ok( value.into() ) + } +} + +impl< T, Marker > quote::ToTokens for AttributePropertySyn< T, Marker > +where + T : syn::parse::Parse + quote::ToTokens, +{ + fn to_tokens( &self, tokens : &mut proc_macro2::TokenStream ) + { + self.0.to_tokens( tokens ); + } +} + +impl< T, Marker > core::ops::Deref for AttributePropertySyn< T, Marker > +where T : syn::parse::Parse + quote::ToTokens +{ + type Target = T; + #[ inline( always ) ] + fn deref( &self ) -> &T + { + &self.0 + } +} + +impl< T, Marker > AsRef< T > for AttributePropertySyn< T, Marker > +where T : syn::parse::Parse + quote::ToTokens +{ + #[ inline( always ) ] + fn as_ref( &self ) -> &T + { + &self.0 + } +} + +impl< T, Marker > From< T > for AttributePropertySyn< T, Marker > +where T : syn::parse::Parse + quote::ToTokens +{ + #[ inline( always ) ] + fn from( src : T ) -> Self + { + Self( src, Default::default() ) + } +} diff --git a/module/core/macro_tools/src/attr_prop/syn_optional.rs b/module/core/macro_tools/src/attr_prop/syn_optional.rs new file mode 100644 index 0000000000..d595e9496a --- /dev/null +++ b/module/core/macro_tools/src/attr_prop/syn_optional.rs @@ -0,0 +1,160 @@ +//! +//! Property of an attribute which simply wraps one of the standard `syn` types and keeps it optional. +//! + +use crate::*; +use former_types::Assign; + +/// Default marker for `AttributePropertyOptionalSyn`. +/// Used if no marker is defined as parameter. +#[ derive( Debug, Default, Clone, Copy ) ] +pub struct AttributePropertyOptionalSynMarker; + +/// +/// Property of an attribute which simply wraps one of the standard `syn` types and keeps it optional. +/// + +#[ derive( Debug, Clone ) ] +pub struct AttributePropertyOptionalSyn< T, Marker = AttributePropertyOptionalSynMarker >( Option< T >, ::core::marker::PhantomData< Marker > ) +where + T : syn::parse::Parse + quote::ToTokens; + +impl< T, Marker > AttributePropertyOptionalSyn< T, Marker > +where + T : syn::parse::Parse + quote::ToTokens, +{ + /// Just unwraps and returns the internal data. + #[ inline( always ) ] + pub fn internal( self ) -> Option< T > + { + self.0 + } + + /// Returns an Option reference to the internal data. + #[ inline( always ) ] + pub fn ref_internal( &self ) -> Option< &T > + { + self.0.as_ref() + } +} + +impl< T, Marker, IntoT > Assign< AttributePropertyOptionalSyn< T, Marker >, IntoT > +for AttributePropertyOptionalSyn< T, Marker > +where + T : syn::parse::Parse + quote::ToTokens, + IntoT : Into< AttributePropertyOptionalSyn< T, Marker > >, +{ + /// Inserts value of another instance into the option if it is None, then returns a mutable reference to the contained value. + /// If another instance does is None then do nothing. + #[ inline( always ) ] + fn assign( &mut self, component : IntoT ) + { + let component = component.into(); + match component.0 + { + Some( val ) => { self.0 = Some( val ); }, + None => {}, + } + } +} + +impl< T, Marker > AttributePropertyComponent for AttributePropertyOptionalSyn< T, Marker > +where + T : syn::parse::Parse + quote::ToTokens, + Marker : AttributePropertyComponent, +{ + const KEYWORD : &'static str = Marker::KEYWORD; +} + +impl< T, Marker > Default for AttributePropertyOptionalSyn< T, Marker > +where + T : syn::parse::Parse + quote::ToTokens, +{ + fn default() -> Self + { + Self( None, Default::default() ) + } +} + +impl< T, Marker > syn::parse::Parse for AttributePropertyOptionalSyn< T, Marker > +where + T : syn::parse::Parse + quote::ToTokens, +{ + fn parse( input : syn::parse::ParseStream< '_ > ) -> syn::Result< Self > + { + input.parse::< syn::Token![ = ] >()?; + let value : T = input.parse()?; + Ok( value.into() ) + } +} + +impl< T, Marker > quote::ToTokens for AttributePropertyOptionalSyn< T, Marker > +where + T : syn::parse::Parse + quote::ToTokens, +{ + fn to_tokens( &self, tokens : &mut proc_macro2::TokenStream ) + { + self.0.to_tokens( tokens ); + } +} + +impl< T, Marker > core::ops::Deref for AttributePropertyOptionalSyn< T, Marker > +where T : syn::parse::Parse + quote::ToTokens +{ + type Target = Option< T >; + #[ inline( always ) ] + fn deref( &self ) -> &Option< T > + { + &self.0 + } +} + +impl< T, Marker > AsRef< Option< T > > for AttributePropertyOptionalSyn< T, Marker > +where T : syn::parse::Parse + quote::ToTokens +{ + #[ inline( always ) ] + fn as_ref( &self ) -> &Option< T > + { + &self.0 + } +} + +impl< T, Marker > From< T > for AttributePropertyOptionalSyn< T, Marker > +where T : syn::parse::Parse + quote::ToTokens +{ + #[ inline( always ) ] + fn from( src : T ) -> Self + { + Self( Some( src ), Default::default() ) + } +} + +impl< T, Marker > From< Option< T > > for AttributePropertyOptionalSyn< T, Marker > +where T : syn::parse::Parse + quote::ToTokens +{ + #[ inline( always ) ] + fn from( src : Option< T > ) -> Self + { + Self( src, Default::default() ) + } +} + +impl< T, Marker > From< AttributePropertyOptionalSyn< T, Marker > > for Option< T > +where T : syn::parse::Parse + quote::ToTokens +{ + #[ inline( always ) ] + fn from( src : AttributePropertyOptionalSyn< T, Marker > ) -> Self + { + src.0 + } +} + +impl< 'a, T, Marker > From< &'a AttributePropertyOptionalSyn< T, Marker > > for Option< &'a T > +where T : syn::parse::Parse + quote::ToTokens +{ + #[ inline( always ) ] + fn from( src : &'a AttributePropertyOptionalSyn< T, Marker > ) -> Self + { + src.0.as_ref() + } +} diff --git a/module/core/macro_tools/src/derive.rs b/module/core/macro_tools/src/derive.rs index 9f29444e48..13db46db90 100644 --- a/module/core/macro_tools/src/derive.rs +++ b/module/core/macro_tools/src/derive.rs @@ -5,7 +5,7 @@ /// Internal namespace. pub( crate ) mod private { - use super::super::*; + use crate::*; use syn::punctuated::Punctuated; /// diff --git a/module/core/macro_tools/src/diag.rs b/module/core/macro_tools/src/diag.rs index 739d92f527..10a7e9e0a5 100644 --- a/module/core/macro_tools/src/diag.rs +++ b/module/core/macro_tools/src/diag.rs @@ -5,7 +5,7 @@ /// Internal namespace. pub( crate ) mod private { - use super::super::*; + use crate::*; /// /// Result with syn::Error. @@ -229,13 +229,13 @@ pub( crate ) mod private #[ macro_export ] macro_rules! tree_print { - ( $src:expr ) => + ( $src :expr ) => {{ let result = $crate::tree_diagnostics_str!( $src ); println!( "{}", result ); result }}; - ( $( $src:expr ),+ $(,)? ) => + ( $( $src :expr ),+ $(,)? ) => {{ $( $crate::tree_print!( $src ) );+ }}; @@ -257,13 +257,13 @@ pub( crate ) mod private #[ macro_export ] macro_rules! code_print { - ( $src:expr ) => + ( $src :expr ) => {{ let result = $crate::code_diagnostics_str!( $src ); println!( "{}", result ); result }}; - ( $( $src:expr ),+ $(,)? ) => + ( $( $src :expr ),+ $(,)? ) => {{ $( $crate::code_print!( $src ) );+ }}; @@ -276,7 +276,7 @@ pub( crate ) mod private #[ macro_export ] macro_rules! tree_diagnostics_str { - ( $src:expr ) => + ( $src :expr ) => {{ let src2 = &$src; format!( "{} : {} :\n{:#?}", stringify!( $src ), $crate::qt!{ #src2 }, $src ) @@ -290,7 +290,7 @@ pub( crate ) mod private #[ macro_export ] macro_rules! code_diagnostics_str { - ( $src:expr ) => + ( $src :expr ) => {{ let src2 = &$src; format!( "{} : {}", stringify!( $src ), $crate::qt!{ #src2 } ) @@ -304,7 +304,7 @@ pub( crate ) mod private #[ macro_export ] macro_rules! code_to_str { - ( $src:expr ) => + ( $src :expr ) => {{ let src2 = &$src; format!( "{}", $crate::qt!{ #src2 } ) diff --git a/module/core/macro_tools/src/drop.rs b/module/core/macro_tools/src/drop.rs new file mode 100644 index 0000000000..f6e5814e48 --- /dev/null +++ b/module/core/macro_tools/src/drop.rs @@ -0,0 +1,96 @@ +//! +//! zzz : write +//! + +// zzz : investiage and reuse for iterating +// https://docs.rs/syn/latest/src/syn/punctuated.rs.html#724 +// https://docs.rs/syn/latest/src/syn/drops.rs.html#11-16 + +/// Internal namespace. +pub mod private +{ + // use crate::*; + +// /// zzz : write documentation +// #[ repr( transparent ) ] +// pub struct NoDrop< T : ?Sized >( std::mem::ManuallyDrop< T > ); +// +// impl< T > NoDrop< T > +// { +// /// zzz : write documentation +// pub fn new( value : T ) -> Self +// where +// T : TrivialDrop, +// { +// NoDrop( std::mem::ManuallyDrop::new( value ) ) +// } +// } +// +// impl< T : ?Sized > std::ops::Deref for NoDrop< T > +// { +// type Target = T; +// fn deref( &self ) -> &Self::Target +// { +// &self.0 +// } +// } +// +// impl< T : ?Sized > std::ops::DerefMut for NoDrop< T > +// { +// fn deref_mut( &mut self ) -> &mut Self::Target +// { +// &mut self.0 +// } +// } +// +// /// zzz : write documentation +// pub trait TrivialDrop {} +// +// impl< T > TrivialDrop for std::iter::Empty< T > {} +// impl< 'a, T > TrivialDrop for std::slice::Iter< 'a, T > {} +// impl< 'a, T > TrivialDrop for std::slice::IterMut< 'a, T > {} +// impl< 'a, T > TrivialDrop for std::option::IntoIter< &'a T > {} +// impl< 'a, T > TrivialDrop for std::option::IntoIter< &'a mut T > {} + +} + +#[ 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:: + { + // NoDrop, + // TrivialDrop, + }; +} + +/// 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 drop; + #[ 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/equation.rs b/module/core/macro_tools/src/equation.rs new file mode 100644 index 0000000000..36bab1ccab --- /dev/null +++ b/module/core/macro_tools/src/equation.rs @@ -0,0 +1,160 @@ +//! +//! Attributes analyzys and manipulation. +//! + +/// Internal namespace. +pub( crate ) mod private +{ + use crate::*; + + /// Represents an equation parsed from a procedural macro input. + /// + /// This struct models an equation consisting of a left-hand side, an operator, + /// and a right-hand side. The `Equation` is typically constructed during the + /// parsing process of macro input, where the `left` and `op` fields are expected + /// to be syntactically represented by `syn::Path` and `syn::BinOp` respectively, + /// indicating the variable and operation involved. The `right` field is a + /// `proc_macro2::TokenStream`, which can represent more complex expressions + /// including, but not limited to, literals, function calls, or further operations. + /// + /// # Fields + /// - `left`: The left-hand side of the equation, represented as a path. + /// This could be a variable or a more complex path in the code being + /// processed by the macro. + /// + /// - `op`: The binary operator used in the equation, such as addition, + /// subtraction, multiplication, etc. + /// + /// - `right`: The right-hand side of the equation. Given the potential + /// complexity of expressions on this side, it is represented as a + /// `proc_macro2::TokenStream` to accommodate any valid Rust expression. + /// + /// # Examples + /// + /// Parsing an equation from macro input: + /// + /// ```rust + /// use macro_tools::equation; + /// let got : equation::Equation = syn::parse_quote!( default = 31 ); + /// macro_tools::tree_print!( got ); + /// assert_eq!( macro_tools::code_to_str!( got ), "default = 31".to_string() ); + /// ``` + #[ derive( Debug ) ] + pub struct Equation + { + /// The LHS of the equation, represented by a syntactic path. + pub left : syn::Path, + // /// The binary operator (e.g., +, -, *, /) of the equation. + // pub op : syn::BinOp, + /// Equality token. + pub op : syn::Token![ = ], + /// The RHS of the equation, capable of holding complex expressions. + pub right : proc_macro2::TokenStream, + } + + impl syn::parse::Parse for Equation + { + fn parse( input : syn::parse::ParseStream< '_ > ) -> Result< Self > + { + let left : syn::Path = input.parse()?; + let op : syn::Token![ = ] = input.parse()?; + let right : proc_macro2::TokenStream = input.parse()?; + Ok( Equation { left, op, right } ) + } + } + + impl quote::ToTokens for Equation + { + fn to_tokens( &self, tokens : &mut proc_macro2::TokenStream ) + { + self.left.to_tokens( tokens ); + self.op.to_tokens( tokens ); + self.right.to_tokens( tokens ); + } + } + + // impl core::fmt::Display for Equation + // { + // fn fmt( &self, f : &mut core::fmt::Formatter< '_ > ) -> core::fmt::Result + // { + // write!( f, "{}", self.left.to_string() ); + // write!( f, "{}", self.op.to_string() ); + // write!( f, "{}", self.right.to_string() ) + // } + // } + + /// + /// For attribute like `#[former( default = 31 ) ]` return key `default` and value `31`, + /// as well as syn::Meta as the last element of result tuple. + /// + /// ### Basic use-case. + /// + /// ```rust + /// use macro_tools::equation; + /// let attr : syn::Attribute = syn::parse_quote!( #[ former( default = 31 ) ] ); + /// // tree_print!( attr ); + /// let got = equation::from_meta( &attr ).unwrap(); + /// assert_eq!( macro_tools::code_to_str!( got ), "default = 31".to_string() ); + /// ``` + + pub fn from_meta( attr : &syn::Attribute ) -> Result< Equation > + { + let meta = &attr.meta; + return match meta + { + syn::Meta::List( ref meta_list ) => + { + let eq : Equation = syn::parse2( meta_list.tokens.clone() )?; + Ok( eq ) + } + _ => return Err( syn::Error::new( attr.span(), "Unknown format of attribute, expected syn::Meta::List( meta_list )" ) ), + }; + } + +} + +#[ doc( inline ) ] +#[ allow( unused_imports ) ] +pub use protected::*; + +/// Protected namespace of the module. +pub mod protected +{ + #[ doc( inline ) ] + #[ allow( unused_imports ) ] + pub use super::orphan::*; + #[ doc( inline ) ] + #[ allow( unused_imports ) ] + pub use super::private:: + { + from_meta, + }; +} + +/// Orphan namespace of the module. +pub mod orphan +{ + #[ doc( inline ) ] + #[ allow( unused_imports ) ] + pub use super::exposed::*; +} + +/// Exposed namespace of the module. +pub mod exposed +{ + pub use super::protected as equation; + #[ doc( inline ) ] + #[ allow( unused_imports ) ] + pub use super::prelude::*; + #[ doc( inline ) ] + #[ allow( unused_imports ) ] + pub use super::private:: + { + Equation, + }; +} + +/// Prelude to use essentials: `use my_module::prelude::*`. +pub mod prelude +{ +} diff --git a/module/core/macro_tools/src/generic_analyze.rs b/module/core/macro_tools/src/generic_analyze.rs deleted file mode 100644 index 0ab68918ae..0000000000 --- a/module/core/macro_tools/src/generic_analyze.rs +++ /dev/null @@ -1,100 +0,0 @@ -//! -//! Analyze generic to provide more information than trivial syntax node. -//! - -// xxx : is it used? - -/// Internal namespace. -pub( crate ) mod private -{ - - // xxx : qqq : examples. documentation - /// Result of generics analyze. - #[ derive( Debug ) ] - pub struct GenericsAnalysis - { - /// Original generics. - pub generics : syn::Generics, - /// Array of names. - pub names : Vec< syn::Ident >, - } - - /// To analyze generics. - pub trait GenericsAnalyze - { - - /// Analyze generic. - fn generics_analyze( &self ) -> GenericsAnalysis; - - } - - impl GenericsAnalyze for syn::ItemTrait - { - fn generics_analyze( &self ) -> GenericsAnalysis - { - let mut names = vec![]; - let generics = self.generics.clone(); - - for param in &generics.params - { - match param - { - syn::GenericParam::Type( type_param ) => names.push( type_param.ident.clone() ), - syn::GenericParam::Lifetime( lifetime_def ) => names.push( lifetime_def.lifetime.ident.clone() ), - syn::GenericParam::Const( const_param ) => names.push( const_param.ident.clone() ), - } - } - - GenericsAnalysis - { - generics, - names, - } - } - } - -} - -#[ 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::*; -} - -/// 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 -{ - #[ doc( inline ) ] - #[ allow( unused_imports ) ] - pub use super:: - { - prelude::*, - private::GenericsAnalysis, - }; - pub use super::protected as generic_analyze; -} - -/// Prelude to use essentials: `use my_module::prelude::*`. -pub mod prelude -{ - #[ doc( inline ) ] - #[ allow( unused_imports ) ] - pub use super:: - { - private::GenericsAnalyze, - }; -} diff --git a/module/core/macro_tools/src/generic_args.rs b/module/core/macro_tools/src/generic_args.rs index aeea032b5a..56f70d64a6 100644 --- a/module/core/macro_tools/src/generic_args.rs +++ b/module/core/macro_tools/src/generic_args.rs @@ -1,7 +1,6 @@ //! -//! Manipulations on generic arguments. +//! This module provides utilities to handle and manipulate generic arguments using the `syn` crate. It includes traits and functions for transforming, merging, and managing generic parameters within procedural macros, enabling seamless syntactic analysis and code generation. //! -//! xxx : update documentation of file /// Internal namespace. pub( crate ) mod private @@ -148,6 +147,11 @@ pub use protected::*; /// Protected namespace of the module. pub mod protected { + + //! + //! This module provides utilities to handle and manipulate generic arguments using the `syn` crate. It includes traits and functions for transforming, merging, and managing generic parameters within procedural macros, enabling seamless syntactic analysis and code generation. + //! + #[ doc( inline ) ] #[ allow( unused_imports ) ] pub use super::orphan::*; diff --git a/module/core/macro_tools/src/generic_params.rs b/module/core/macro_tools/src/generic_params.rs index f2e852f125..5af6bdc366 100644 --- a/module/core/macro_tools/src/generic_params.rs +++ b/module/core/macro_tools/src/generic_params.rs @@ -1,30 +1,11 @@ //! -//! Manipulations on generic parameters. +//! Functions and structures to handle and manipulate generic parameters using the `syn` crate. It's designed to support macro-driven code generation by simplifying, merging, extracting, and decomposing `syn::Generics`. //! -//! # 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::*; + use crate::*; /// A `GenericsWithWhere` struct to handle the parsing of Rust generics with an explicit `where` clause. /// @@ -34,8 +15,10 @@ pub( crate ) mod private /// 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(); + /// 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() ); /// ``` @@ -214,13 +197,13 @@ pub( crate ) mod private /// 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 ); + /// let simplified_generics = macro_tools::generic_params::only_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 + pub fn only_names( generics : &syn::Generics ) -> syn::Generics { // use syn::{ Generics, GenericParam, LifetimeDef, TypeParam, ConstParam }; use syn::{ Generics, GenericParam, LifetimeParam, TypeParam, ConstParam }; @@ -264,6 +247,50 @@ pub( crate ) mod private result } + /// Extracts the names of type parameters, lifetimes, and const parameters from the given `Generics`. + /// + /// This function returns an iterator over the names of the parameters in the `Generics`, + /// which can be useful for generating code that requires just the names of the parameters + /// without their associated bounds or default values. + /// + /// # Arguments + /// + /// * `generics` - The `Generics` instance from which to extract parameter names. + /// + /// # Returns + /// + /// Returns an iterator over the names of the parameters. + /// + /// # Examples + /// + /// ```rust + /// # use macro_tools::syn::parse_quote; + /// + /// let generics : syn::Generics = parse_quote! + /// { + /// < T : Clone + Default, U, 'a, const N : usize > + /// }; + /// let names : Vec< _ > = macro_tools::generic_params::names( &generics ).collect(); + /// + /// assert_eq!( names, vec! + /// [ + /// &syn::Ident::new( "T", proc_macro2::Span::call_site() ), + /// &syn::Ident::new( "U", proc_macro2::Span::call_site() ), + /// &syn::Ident::new( "a", proc_macro2::Span::call_site() ), + /// &syn::Ident::new( "N", proc_macro2::Span::call_site() ) + /// ]); + /// ``` + + pub fn names< 'a >( generics : &'a syn::Generics ) -> impl IterTrait< 'a, &'a syn::Ident > + Clone + { + generics.params.iter().map( | param | match param + { + syn::GenericParam::Type( type_param ) => &type_param.ident, + syn::GenericParam::Lifetime( lifetime_def ) => &lifetime_def.lifetime.ident, + syn::GenericParam::Const( const_param ) => &const_param.ident, + }) + } + /// Decomposes `syn::Generics` into components suitable for different usage contexts in Rust implementations, /// specifically focusing on different requirements for `impl` blocks and type definitions. /// @@ -275,6 +302,13 @@ pub( crate ) mod private /// This helps in situations where you need different representations of generics for implementing traits, /// defining types, or specifying trait bounds and conditions. /// + /// This function is similar to `syn::Generics::split_for_impl`, which also splits generics into components + /// suitable for `impl` blocks and type definitions. However, `split_for_impl` wraps the tokens in `<>`, which + /// can reduce the flexibility of the results. The `decompose` function provides more control over the output + /// by not wrapping the tokens, allowing for more precise usage in macros and other contexts. + /// Additionally, `decompose` returns an extra component with the generics including defaults, which is often + /// in demand for certain macro or code generation tasks. + /// /// # Examples /// /// ```rust @@ -304,6 +338,47 @@ pub( crate ) mod private /// - `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. + /// + /// # Differences from `syn::Generics::split_for_impl` + /// + /// While both `decompose` and `split_for_impl` functions split generics into components for `impl` blocks, type definitions, and where clauses, + /// there are key differences: + /// - `split_for_impl` wraps the generics in `<>`, which can be limiting when you need to use the generics in a different context or format. + /// - `decompose` provides raw punctuated generic parameters, offering greater flexibility and control over the output format. + /// - `decompose` returns an extra component with the generics including defaults, which is often needed for certain macro or code generation tasks. + /// + /// # Example of function signature using `decompose` + /// + /// ```rust + /// use macro_tools::{ syn, proc_macro2, qt }; + /// + /// fn generate_unit + /// ( + /// item_name : &syn::Ident, + /// generics_with_defaults : syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, + /// generics_impl : syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, + /// generics_ty : syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, + /// generics_where: syn::punctuated::Punctuated< syn::WherePredicate, syn::token::Comma >, + /// ) + /// -> proc_macro2::TokenStream + /// { + /// qt! + /// { + /// #[ automatically_derived ] + /// impl< #generics_impl > From< i32 > for #item_name< #generics_ty > + /// where + /// #generics_where + /// { + /// #[ inline ] + /// fn from( src : i32 ) -> Self + /// { + /// Wrap( src ) + /// } + /// } + /// } + /// } + /// ``` + /// pub fn decompose ( @@ -411,84 +486,19 @@ pub( crate ) mod private ( 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 { + + //! + //! Functions and structures to handle and manipulate generic parameters using the `syn` crate. It's designed to support macro-driven code generation by simplifying, merging, extracting, and decomposing `syn::Generics`. + //! + #[ doc( inline ) ] #[ allow( unused_imports ) ] pub use super::orphan::*; @@ -497,12 +507,12 @@ pub mod protected pub use super::private:: { merge, + only_names, names, decompose, }; } -// xxx : external attr instead of internal? /// Orphan namespace of the module. pub mod orphan { diff --git a/module/core/macro_tools/src/item.rs b/module/core/macro_tools/src/item.rs index 64897375e7..2bd19814b2 100644 --- a/module/core/macro_tools/src/item.rs +++ b/module/core/macro_tools/src/item.rs @@ -1,9 +1,12 @@ -//! xxx : update documentation of file +//! This module provides various utilities and namespaces for working with `syn::Item`, specifically focusing on +//! ensuring syntactical correctness and managing different visibility levels within the code. It includes functions +//! to manipulate the structure of items, handle different kinds of fields, and provide a structured approach to +//! organizing the codebase into different access levels. /// Internal namespace. pub( crate ) mod private { - use super::super::*; + use crate::*; /// Ensures the last field in a struct has a trailing comma. /// @@ -83,7 +86,11 @@ pub( crate ) mod private #[ allow( unused_imports ) ] pub use protected::*; -/// Protected namespace of the module. +// qqq : zzz : make sure documentation look good. generate, review and fix every file +/// This module provides various utilities and namespaces for working with `syn::Item`, specifically focusing on +/// ensuring syntactical correctness and managing different visibility levels within the code. It includes functions +/// to manipulate the structure of items, handle different kinds of fields, and provide a structured approach to +/// organizing the codebase into different access levels. pub mod protected { #[ doc( inline ) ] @@ -97,7 +104,6 @@ pub mod protected }; } -// xxx : external attr instead of internal? /// Orphan namespace of the module. pub mod orphan { @@ -114,6 +120,7 @@ pub mod orphan /// Exposed namespace of the module. pub mod exposed { + // pub use super::protected as item; pub use super::protected as item; #[ doc( inline ) ] #[ allow( unused_imports ) ] diff --git a/module/core/macro_tools/src/item_struct.rs b/module/core/macro_tools/src/item_struct.rs new file mode 100644 index 0000000000..6404e000c0 --- /dev/null +++ b/module/core/macro_tools/src/item_struct.rs @@ -0,0 +1,117 @@ +//! +//! Parse structures, like `struct { a : i32 }`. +//! + +/// Internal namespace. +pub( crate ) mod private +{ + use crate::*; + + /// Extracts the types of each field into a vector. + // pub fn field_types< 'a >( t : &'a syn::ItemStruct ) -> impl IterTrait< 'a, &'a syn::Type > + Clone + pub fn field_types< 'a >( t : &'a syn::ItemStruct ) -> impl IterTrait< 'a, &'a syn::Type > + Clone + { + t.fields.iter().map( | field | &field.ty ) + } + + /// Retrieves the names of each field, if they exist. + // pub fn field_names< 'a >( t : &'a syn::ItemStruct ) -> Option< impl IterTrait< 'a, &'a syn::Ident > + 'a > + pub fn field_names< 'a >( t : &'a syn::ItemStruct ) -> Option< Box< dyn IterTrait< 'a, &'a syn::Ident > + '_ > > + // pub fn field_names< 'a >( t : &'a syn::ItemStruct ) -> Option< Box< dyn IterTrait< 'a, syn::Ident > + '_ > > + // pub fn field_names< 'a >( t : &'a syn::ItemStruct ) -> impl IterTrait< 'a, syn::Ident > + { + match &t.fields + { + syn::Fields::Named( fields ) => Some( Box::new( fields.named.iter().map( | field | field.ident.as_ref().unwrap() ) ) ), + syn::Fields::Unit => Some( Box::new( core::iter::empty() ) ), + _ => None, + } + } + + /// Retrieves the type of the first field of the struct. + /// + /// Returns the type if the struct has at least one field, otherwise returns an error. + pub fn first_field_type( t : &syn::ItemStruct ) -> Result< syn::Type > + { + let maybe_field = match t.fields + { + syn::Fields::Named( ref fields ) => fields.named.first(), + syn::Fields::Unnamed( ref fields ) => fields.unnamed.first(), + _ => return Err( syn_err!( t.fields.span(), "Expects either named or unnamed field" ) ), + }; + + if let Some( field ) = maybe_field + { + return Ok( field.ty.clone() ) + } + + return Err( syn_err!( t.span(), "Expects at least one field" ) ); + } + + /// Retrieves the name of the first field of the struct, if available. + /// + /// Returns `Some` with the field identifier for named fields, or `None` for unnamed fields. + /// Returns an error if the struct has no fields + pub fn first_field_name( t : &syn::ItemStruct ) -> Result< Option< syn::Ident > > + { + let maybe_field = match t.fields + { + syn::Fields::Named( ref fields ) => fields.named.first(), + syn::Fields::Unnamed( ref fields ) => fields.unnamed.first(), + _ => return Err( syn_err!( t.fields.span(), "Expects fields" ) ), + }; + + if let Some( field ) = maybe_field + { + return Ok( field.ident.clone() ) + } + + return Err( syn_err!( t.span(), "Expects type for 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:: + { + // fields_many, + field_types, + field_names, + first_field_type, + first_field_name, + }; +} + +/// 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 item_struct; + #[ 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/iter.rs b/module/core/macro_tools/src/iter.rs new file mode 100644 index 0000000000..59d4d614f3 --- /dev/null +++ b/module/core/macro_tools/src/iter.rs @@ -0,0 +1,126 @@ +//! +//! Iterators. +//! + +/// Internal namespace. +pub( crate ) mod private +{ + // use crate::*; + + /// Trait that encapsulates an iterator with specific characteristics, tailored for use with the `syn` crate. + /// + /// The `IterTrait` trait is designed to represent iterators that may yield references to items (`&'a T`) within the `syn` crate. + /// These iterators must also implement the `ExactSizeIterator` and `DoubleEndedIterator` traits. + /// This combination ensures that the iterator can: + /// - Provide an exact size hint (`ExactSizeIterator`), + /// - Be traversed from both ends (`DoubleEndedIterator`). + /// + pub trait IterTrait< 'a, T > + where + T : 'a, + Self : Iterator< Item = T > + ExactSizeIterator< Item = T > + DoubleEndedIterator, + { + // fn clone_box( &self ) -> Box< dyn IterTrait< 'a, T > + 'a >; + } + + impl< 'a, T, I > IterTrait< 'a, T > for I + where + T : 'a, + Self : Iterator< Item = T > + ExactSizeIterator< Item = T > + DoubleEndedIterator // + Clone, + { + + // fn clone_box( &self ) -> Box< dyn IterTrait< 'a, T > + 'a > + // { + // Box::new( self.clone() ) + // } + + } + +// /// Trait that encapsulates an iterator with specific characteristics, tailored for use with the `syn` crate. +// /// +// /// The `IterTrait2` trait is designed to represent iterators that yield references to items (`&'a T`) within the `syn` crate. +// /// These iterators must also implement the `ExactSizeIterator` and `DoubleEndedIterator` traits. +// /// This combination ensures that the iterator can: +// /// - Provide an exact size hint (`ExactSizeIterator`), +// /// - Be traversed from both ends (`DoubleEndedIterator`). +// /// +// pub trait IterTrait2< T > +// where +// Self : Iterator< Item = T > + ExactSizeIterator< Item = T > + DoubleEndedIterator, +// { +// } +// +// impl< T, I > IterTrait2< T > for I +// where +// Self : Iterator< Item = T > + ExactSizeIterator< Item = T > + DoubleEndedIterator, +// { +// } +// +// /// Trait that encapsulates an iterator with specific characteristics, tailored for use with the `syn` crate. +// /// +// /// The `IterTrait3` trait is designed to represent iterators that yield references to items (`&'a T`) within the `syn` crate. +// /// These iterators must also implement the `ExactSizeIterator` and `DoubleEndedIterator` traits. +// /// This combination ensures that the iterator can: +// /// - Provide an exact size hint (`ExactSizeIterator`), +// /// - Be traversed from both ends (`DoubleEndedIterator`). +// /// +// pub trait IterTrait3< 'a, T : 'a > +// where +// Self : Iterator< Item = T > + ExactSizeIterator< Item = T > + DoubleEndedIterator, +// { +// } +// +// impl< 'a, T : 'a, I > IterTrait3< 'a, T > for I +// where +// Self : Iterator< Item = T > + ExactSizeIterator< Item = T > + DoubleEndedIterator, +// { +// } + +} + +#[ doc( inline ) ] +#[ allow( unused_imports ) ] +pub use protected::*; + +/// Protected namespace of the module. +pub mod protected +{ + #[ doc( inline ) ] + #[ allow( unused_imports ) ] + pub use super::orphan::*; + #[ doc( inline ) ] + #[ allow( unused_imports ) ] + pub use super::private:: + { + }; +} + +/// Orphan namespace of the module. +pub mod orphan +{ + #[ doc( inline ) ] + #[ allow( unused_imports ) ] + pub use super::exposed::*; +} + +/// Exposed namespace of the module. +pub mod exposed +{ + pub use super::protected as iter; + #[ doc( inline ) ] + #[ allow( unused_imports ) ] + pub use super::prelude::*; + #[ doc( inline ) ] + #[ allow( unused_imports ) ] + pub use super::private:: + { + IterTrait, + // IterTrait2, + // IterTrait3, + }; +} + +/// 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 d447b4b67b..85bafc8eb4 100644 --- a/module/core/macro_tools/src/lib.rs +++ b/module/core/macro_tools/src/lib.rs @@ -3,27 +3,48 @@ #![ doc( html_root_url = "https://docs.rs/proc_macro_tools/latest/proc_macro_tools/" ) ] #![ doc = include_str!( concat!( env!( "CARGO_MANIFEST_DIR" ), "/", "Readme.md" ) ) ] +// qqq : review every page of generated documentation improve how it look as well as its content +// +// attr +// Protected namespace of the module. +// container_kind +// Protected namespace of the module. +// dependency +// Dependencies of the module. +// derive +// Protected namespace of the module. +// diag +// Protected namespace of the module. +// drop +// Protected namespace of the module. +// exposed +// Exposed namespace of the module. + /// Modular files. #[ cfg( feature = "enabled" ) ] #[ path = "." ] mod file { - use super::*; + // use super::*; pub mod attr; + pub mod attr_prop; pub mod container_kind; pub mod derive; pub mod diag; - pub mod generic_analyze; + pub mod drop; + pub mod equation; pub mod generic_args; pub mod generic_params; pub mod item; + pub mod item_struct; + pub mod iter; pub mod name; pub mod phantom; pub mod punctuated; pub mod quantifier; + pub mod struct_like; pub mod tokens; pub mod typ; - pub mod type_struct; } /// @@ -59,20 +80,24 @@ pub mod protected pub use super::file:: { attr::orphan::*, + attr_prop::orphan::*, container_kind::orphan::*, derive::orphan::*, diag::orphan::*, - generic_analyze::orphan::*, + drop::orphan::*, + equation::orphan::*, generic_args::orphan::*, generic_params::orphan::*, item::orphan::*, + item_struct::orphan::*, + iter::orphan::*, name::orphan::*, phantom::orphan::*, punctuated::orphan::*, quantifier::orphan::*, + struct_like::orphan::*, tokens::orphan::*, typ::orphan::*, - type_struct::orphan::*, }; } @@ -89,6 +114,7 @@ pub mod orphan #[ cfg( feature = "enabled" ) ] pub mod exposed { + #[ doc( inline ) ] #[ allow( unused_imports ) ] pub use quote:: @@ -97,32 +123,39 @@ pub mod exposed quote, quote_spanned, }; + #[ doc( inline ) ] #[ allow( unused_imports ) ] pub use super:: { prelude::*, }; + #[ doc( inline ) ] #[ allow( unused_imports ) ] pub use super::file:: { attr::exposed::*, + attr_prop::exposed::*, container_kind::exposed::*, derive::orphan::*, diag::exposed::*, - generic_analyze::exposed::*, + drop::exposed::*, + equation::exposed::*, generic_args::exposed::*, generic_params::exposed::*, item::exposed::*, + item_struct::exposed::*, + iter::exposed::*, name::exposed::*, phantom::exposed::*, punctuated::exposed::*, quantifier::exposed::*, + struct_like::exposed::*, tokens::exposed::*, typ::exposed::*, - type_struct::exposed::*, }; + } /// Prelude to use essentials: `use my_module::prelude::*`. @@ -137,18 +170,23 @@ pub mod prelude #[ doc( inline ) ] #[ allow( unused_imports ) ] pub use ::syn; + #[ doc( inline ) ] #[ allow( unused_imports ) ] pub use ::proc_macro2; + #[ doc( inline ) ] #[ allow( unused_imports ) ] pub use ::quote; + #[ doc( inline ) ] #[ allow( unused_imports ) ] - pub use ::quote::quote as qt; - #[ doc( inline ) ] - #[ allow( unused_imports ) ] - pub use ::syn::parse_quote as parse_qt; + pub use ::quote:: + { + quote as qt, + format_ident, + }; + #[ doc( inline ) ] #[ allow( unused_imports ) ] pub use ::syn::spanned::Spanned; @@ -166,7 +204,9 @@ pub mod prelude parenthesized, parse_macro_input, parse_quote, + parse_quote as parse_qt, parse_quote_spanned, + parse_quote_spanned as parse_qt_spanned, }; #[ doc( inline ) ] @@ -174,20 +214,24 @@ pub mod prelude pub use super::file:: { attr::prelude::*, + attr_prop::prelude::*, container_kind::prelude::*, derive::orphan::*, diag::prelude::*, - generic_analyze::prelude::*, + drop::prelude::*, + equation::prelude::*, generic_args::prelude::*, generic_params::prelude::*, item::prelude::*, + item_struct::prelude::*, + iter::prelude::*, name::prelude::*, phantom::prelude::*, punctuated::prelude::*, quantifier::prelude::*, + struct_like::prelude::*, tokens::prelude::*, typ::prelude::*, - type_struct::prelude::*, }; } diff --git a/module/core/macro_tools/src/phantom.rs b/module/core/macro_tools/src/phantom.rs index 7c9e3bbbda..32f2de06b0 100644 --- a/module/core/macro_tools/src/phantom.rs +++ b/module/core/macro_tools/src/phantom.rs @@ -1,9 +1,13 @@ -//! xxx : update documentation of file +//! +//! Responsible for generating marker `PhantomData` fields to avoid the rule requiring the usage of all generic parameters in a struct. This is often necessary to ensure that Rust's type system correctly tracks the ownership and lifetimes of these parameters without needing them to be explicitly used in the struct's fields. +//! +//! Functions and structures to handle and manipulate `PhantomData` fields in structs using the `syn` crate. These utilities ensure that generic parameters are correctly accounted for in type checking, even if they are not directly used in the struct's fields. +//! /// Internal namespace. pub( crate ) mod private { - use super::super::*; + use crate::*; /// Adds a `PhantomData` field to a struct to manage generic parameter usage. /// @@ -24,7 +28,7 @@ pub( crate ) mod private /// ```rust /// use syn::{ parse_quote, ItemStruct }; /// - /// let input_struct: ItemStruct = parse_quote! + /// let input_struct : ItemStruct = parse_quote! /// { /// pub struct MyStruct< T, U > /// { @@ -116,7 +120,7 @@ pub( crate ) mod private /// 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 ) > + /// // Output : ::core::marker::PhantomData< ( &'a (), *const T, N ) > /// ``` /// pub fn tuple( input : &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma > ) -> syn::Type @@ -165,7 +169,7 @@ pub( crate ) mod private let result : syn::Type = syn::parse_quote! { - core::marker::PhantomData< #generics_tuple_type > + ::core::marker::PhantomData< #generics_tuple_type > }; result @@ -177,9 +181,15 @@ pub( crate ) mod private #[ allow( unused_imports ) ] pub use protected::*; -/// Protected namespace of the module. pub mod protected { + + //! + //! Responsible for generating marker `PhantomData` fields to avoid the rule requiring the usage of all generic parameters in a struct. This is often necessary to ensure that Rust's type system correctly tracks the ownership and lifetimes of these parameters without needing them to be explicitly used in the struct's fields. + //! + //! Functions and structures to handle and manipulate `PhantomData` fields in structs using the `syn` crate. These utilities ensure that generic parameters are correctly accounted for in type checking, even if they are not directly used in the struct's fields. + //! + #[ doc( inline ) ] #[ allow( unused_imports ) ] pub use super::orphan::*; @@ -192,7 +202,6 @@ pub mod protected }; } -// xxx : external attr instead of internal? /// Orphan namespace of the module. pub mod orphan { diff --git a/module/core/macro_tools/src/punctuated.rs b/module/core/macro_tools/src/punctuated.rs index 5ea50730c4..0eb1eb50dd 100644 --- a/module/core/macro_tools/src/punctuated.rs +++ b/module/core/macro_tools/src/punctuated.rs @@ -1,4 +1,8 @@ -// ! xxx : write description +//! +//! Structures and functions for handling `syn::punctuated::Punctuated` collections. +//! +//! This module provides functionality to manipulate and ensure correct punctuation in `syn::punctuated::Punctuated` collections, commonly used in procedural macros to represent sequences of elements separated by punctuation marks, such as commas. +//! /// Internal namespace. pub( crate ) mod private @@ -20,9 +24,14 @@ pub( crate ) mod private #[ allow( unused_imports ) ] pub use protected::*; -/// Protected namespace of the module. pub mod protected { + //! + //! Structures and functions for handling `syn::punctuated::Punctuated` collections. + //! + //! This module provides functionality to manipulate and ensure correct punctuation in `syn::punctuated::Punctuated` collections, commonly used in procedural macros to represent sequences of elements separated by punctuation marks, such as commas. + //! + #[ doc( inline ) ] #[ allow( unused_imports ) ] pub use super::orphan::*; diff --git a/module/core/macro_tools/src/quantifier.rs b/module/core/macro_tools/src/quantifier.rs index d880ee9eb2..15901b7f89 100644 --- a/module/core/macro_tools/src/quantifier.rs +++ b/module/core/macro_tools/src/quantifier.rs @@ -5,7 +5,7 @@ /// Internal namespace. pub( crate ) mod private { - use super::super::*; + use crate::*; /// /// Marker saying how to parse several elements of such type in a row. @@ -331,6 +331,7 @@ pub mod exposed #[ allow( unused_imports ) ] pub use super::private:: { + AsMuchAsPossibleNoDelimiter, Pair, Many, }; @@ -343,6 +344,5 @@ pub mod prelude #[ allow( unused_imports ) ] pub use super::private:: { - AsMuchAsPossibleNoDelimiter, }; } diff --git a/module/core/macro_tools/src/struct_like.rs b/module/core/macro_tools/src/struct_like.rs new file mode 100644 index 0000000000..55b90ba0eb --- /dev/null +++ b/module/core/macro_tools/src/struct_like.rs @@ -0,0 +1,467 @@ +//! +//! Parse structures, like `struct { a : i32 }`. +//! + +/// Internal namespace. +pub( crate ) mod private +{ + use crate::*; + // use interval_adapter::BoundExt; + + /// Enum to encapsulate either a field from a struct or a variant from an enum. + #[ derive( Debug, PartialEq ) ] + pub enum FieldOrVariant< 'a > + { + /// Represents a field within a struct or union. + Field( &'a syn::Field ), + /// Represents a variant within an enum. + Variant( &'a syn::Variant ), + } + + impl< 'a > From< &'a syn::Field > for FieldOrVariant< 'a > + { + fn from( field : &'a syn::Field ) -> Self + { + FieldOrVariant::Field( field ) + } + } + + impl< 'a > From< &'a syn::Variant > for FieldOrVariant< 'a > + { + fn from( variant : &'a syn::Variant ) -> Self + { + FieldOrVariant::Variant( variant ) + } + } + + impl quote::ToTokens for FieldOrVariant< '_ > + { + fn to_tokens( &self, tokens : &mut proc_macro2::TokenStream ) + { + match self + { + FieldOrVariant::Field( item ) => + { + item.to_tokens( tokens ); + }, + FieldOrVariant::Variant( item ) => + { + item.to_tokens( tokens ); + }, + } + } + } + + impl< 'a > FieldOrVariant< 'a > + { + + /// Returns a reference to the attributes of the item. + pub fn attrs( &self ) -> &Vec< syn::Attribute > + { + match self + { + FieldOrVariant::Field( e ) => &e.attrs, + FieldOrVariant::Variant( e ) => &e.attrs, + } + } + + /// Returns a reference to the visibility of the item. + pub fn vis( &self ) -> Option< &syn::Visibility > + { + match self + { + FieldOrVariant::Field( e ) => Some( &e.vis ), + FieldOrVariant::Variant( _ ) => None, + } + } + + /// Returns a reference to the mutability of the item. + pub fn mutability( &self ) -> Option< &syn::FieldMutability > + { + match self + { + FieldOrVariant::Field( e ) => Some( &e.mutability ), + FieldOrVariant::Variant( _ ) => None, + } + } + + /// Returns a reference to the identifier of the item. + pub fn ident( &self ) -> Option< &syn::Ident > + { + match self + { + FieldOrVariant::Field( e ) => e.ident.as_ref(), + FieldOrVariant::Variant( e ) => Some( &e.ident ), + } + } + + /// Returns an iterator over elements of the item. + pub fn typ( &self ) -> Option< &syn::Type > + { + match self + { + FieldOrVariant::Field( e ) => + { + Some( &e.ty ) + }, + FieldOrVariant::Variant( _e ) => + { + None + }, + } + } + + /// Returns a reference to the fields of the item. + pub fn fields( &self ) -> Option< &syn::Fields > + { + match self + { + FieldOrVariant::Field( _ ) => None, + FieldOrVariant::Variant( e ) => Some( &e.fields ), + } + } + + /// Returns a reference to the discriminant of the item. + pub fn discriminant( &self ) -> Option< &( syn::token::Eq, syn::Expr ) > + { + match self + { + FieldOrVariant::Field( _ ) => None, + FieldOrVariant::Variant( e ) => e.discriminant.as_ref(), + } + } + + } + + /// Represents various struct-like constructs in Rust code. + /// + /// This enum enables differentiation among unit types, structs, and enums, allowing + /// for syntactic analysis and manipulation within macros. `StructLike` is designed to be + /// used in macro contexts where behaviors may vary based on the struct-like type being processed. + /// + /// Variants: + /// - `Unit`: Represents unit structs, which are types without any fields or data. Useful in scenarios where + /// a type needs to exist but does not hold any data itself, typically used for type-safe markers. + /// - `Struct`: Represents regular Rust structs that contain fields. This variant is used to handle data structures + /// that hold multiple related data pieces together in a named format. + /// - `Enum`: Represents enums in Rust, which are types that can hold one of multiple possible variants. This is particularly + /// useful for type-safe state or option handling without the use of external discriminators. + /// + #[ derive( Debug, PartialEq ) ] + pub enum StructLike + { + /// A unit struct with no fields. + Unit( syn::ItemStruct ), + /// A typical Rust struct with named fields. + Struct( syn::ItemStruct ), + /// A Rust enum, which can be one of several defined variants. + Enum( syn::ItemEnum ), + } + + impl From< syn::ItemStruct > for StructLike + { + fn from( item_struct : syn::ItemStruct ) -> Self + { + if item_struct.fields.is_empty() + { + StructLike::Unit( item_struct ) + } + else + { + StructLike::Struct( item_struct ) + } + } + } + + impl From< syn::ItemEnum > for StructLike + { + fn from( item_enum : syn::ItemEnum ) -> Self + { + StructLike::Enum( item_enum ) + } + } + + impl syn::parse::Parse for StructLike + { + fn parse( input : syn::parse::ParseStream< '_ > ) -> syn::Result< Self > + { + use syn::{ ItemStruct, ItemEnum, Visibility, Attribute }; + + // Parse attributes + let attributes : Vec< Attribute > = input.call( Attribute::parse_outer )?; + // Parse visibility + let visibility : Visibility = input.parse().unwrap_or( syn::Visibility::Inherited ); + + // Fork input stream to handle struct/enum keyword without consuming + let lookahead = input.lookahead1(); + if lookahead.peek( syn::Token![ struct ] ) + { + // Parse ItemStruct + let mut item_struct : ItemStruct = input.parse()?; + item_struct.vis = visibility; + item_struct.attrs = attributes.into(); + if item_struct.fields.is_empty() + { + Ok( StructLike::Unit( item_struct ) ) + } + else + { + Ok( StructLike::Struct( item_struct ) ) + } + } + else if lookahead.peek( syn::Token![ enum ] ) + { + // Parse ItemEnum + let mut item_enum : ItemEnum = input.parse()?; + item_enum.vis = visibility; + item_enum.attrs = attributes.into(); + Ok( StructLike::Enum( item_enum ) ) + } + else + { + Err( lookahead.error() ) + } + } + } + + impl quote::ToTokens for StructLike + { + fn to_tokens( &self, tokens : &mut proc_macro2::TokenStream ) + { + match self + { + StructLike::Unit( item ) | StructLike::Struct( item ) => + { + item.to_tokens( tokens ); + }, + StructLike::Enum( item ) => + { + item.to_tokens( tokens ); + }, + } + } + } + + impl StructLike + { + + /// Returns an iterator over elements of the item. + pub fn elements< 'a >( &'a self ) -> impl IterTrait< 'a, FieldOrVariant< 'a > > + 'a + { + match self + { + StructLike::Unit( _ ) => + { + let empty : Vec< FieldOrVariant< 'a > > = vec![]; + Box::new( empty.into_iter() ) as Box< dyn IterTrait< 'a, FieldOrVariant< 'a > > > + }, + StructLike::Struct( item ) => + { + let fields = item.fields.iter().map( FieldOrVariant::from ); + Box::new( fields ) as Box< dyn IterTrait< 'a, FieldOrVariant< 'a > > > + }, + StructLike::Enum( item ) => + { + let variants = item.variants.iter().map( FieldOrVariant::from ); + Box::new( variants ) as Box< dyn IterTrait< 'a, FieldOrVariant< 'a > > > + }, + } + } + + /// Returns an iterator over elements of the item. + pub fn attrs( &self ) -> &Vec< syn::Attribute > + { + match self + { + StructLike::Unit( item ) => + { + &item.attrs + }, + StructLike::Struct( item ) => + { + &item.attrs + }, + StructLike::Enum( item ) => + { + &item.attrs + }, + } + } + + /// Returns an iterator over elements of the item. + pub fn vis( &self ) -> &syn::Visibility + { + match self + { + StructLike::Unit( item ) => + { + &item.vis + }, + StructLike::Struct( item ) => + { + &item.vis + }, + StructLike::Enum( item ) => + { + &item.vis + }, + } + } + + /// Returns an iterator over elements of the item. + pub fn ident( &self ) -> &syn::Ident + { + match self + { + StructLike::Unit( item ) => + { + &item.ident + }, + StructLike::Struct( item ) => + { + &item.ident + }, + StructLike::Enum( item ) => + { + &item.ident + }, + } + } + + /// Returns an iterator over elements of the item. + pub fn generics( &self ) -> &syn::Generics + { + match self + { + StructLike::Unit( item ) => + { + &item.generics + }, + StructLike::Struct( item ) => + { + &item.generics + }, + StructLike::Enum( item ) => + { + &item.generics + }, + } + } + + /// Returns an iterator over fields of the item. + // pub fn fields( &self ) -> Box< dyn Iterator< Item = &syn::Field > + '_ > + pub fn fields< 'a >( &'a self ) -> Box< dyn IterTrait< 'a, &'a syn::Field > + '_ > + // pub fn fields< 'a >( &'a self ) -> impl IterTrait< 'a, &'a syn::Field > + { + match self + { + StructLike::Unit( _item ) => + { + Box::new( std::iter::empty() ) + }, + StructLike::Struct( item ) => + { + Box::new( item.fields.iter() ) + }, + StructLike::Enum( _item ) => + { + Box::new( std::iter::empty() ) + }, + } + } + + /// Extracts the name of each field. + // pub fn field_names( &self ) -> Box< dyn Iterator< Item = Option< &syn::Ident > > + '_ > + // pub fn field_names< 'a >( &'a self ) -> Box< dyn IterTrait< 'a, Option< &'a syn::Ident > > + '_ > + // pub fn field_names< 'a >( &'a self ) -> Option< Box< dyn IterTrait< 'a, &'a syn::Ident > + '_ > > + pub fn field_names< 'a >( &'a self ) -> Option< impl IterTrait< 'a, &'a syn::Ident > + '_ > + { + match self + { + StructLike::Unit( item ) => + { + item_struct::field_names( item ) + }, + StructLike::Struct( item ) => + { + item_struct::field_names( item ) + }, + StructLike::Enum( _item ) => + { + Some( Box::new( self.fields().map( | field | field.ident.as_ref().unwrap() ) ) ) + // Box::new( std::iter::empty() ) + }, + } + + // Box::new( self.fields().map( | field | field.ident.as_ref() ) ) + } + + /// Extracts the type of each field. + // pub fn field_types( &self ) -> Box< dyn Iterator< Item = &syn::Type > + '_ > + // pub fn field_types< 'a >( &'a self ) -> Box< dyn IterTrait< 'a, &'a syn::Type > + '_ > + pub fn field_types< 'a >( &'a self ) -> impl IterTrait< 'a, &'a syn::Type > + { + Box::new( self.fields().map( | field | &field.ty ) ) + } + + /// Extracts the name of each field. + // pub fn field_attrs( &self ) -> Box< dyn Iterator< Item = &Vec< syn::Attribute > > + '_ > + // pub fn field_attrs< 'a >( &'a self ) -> Box< dyn IterTrait< 'a, &'a Vec< syn::Attribute > > + '_ > + pub fn field_attrs< 'a >( &'a self ) -> impl IterTrait< 'a, &'a Vec< syn::Attribute > > + { + Box::new( self.fields().map( | field | &field.attrs ) ) + } + + /// Extract the first field. + pub fn first_field( &self ) -> Option< &syn::Field > + { + self.fields().next() + // .ok_or( syn_err!( self.span(), "Expects at least one field" ) ) + } + + } + + // + +} + +#[ 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:: + { + StructLike, + FieldOrVariant, + }; +} + +/// 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 struct_like; + #[ 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 9f0cd32435..7a09fc4689 100644 --- a/module/core/macro_tools/src/tokens.rs +++ b/module/core/macro_tools/src/tokens.rs @@ -5,7 +5,7 @@ /// Internal namespace. pub( crate ) mod private { - use super::super::*; + use crate::*; use core::fmt; /// `Tokens` is a wrapper around `proc_macro2::TokenStream`. @@ -71,82 +71,6 @@ pub( crate ) mod private } } - /// Represents an equation parsed from a procedural macro input. - /// - /// This struct models an equation consisting of a left-hand side, an operator, - /// and a right-hand side. The `Equation` is typically constructed during the - /// parsing process of macro input, where the `left` and `op` fields are expected - /// to be syntactically represented by `syn::Path` and `syn::BinOp` respectively, - /// indicating the variable and operation involved. The `right` field is a - /// `proc_macro2::TokenStream`, which can represent more complex expressions - /// including, but not limited to, literals, function calls, or further operations. - /// - /// # Fields - /// - `left`: The left-hand side of the equation, represented as a path. - /// This could be a variable or a more complex path in the code being - /// processed by the macro. - /// - /// - `op`: The binary operator used in the equation, such as addition, - /// subtraction, multiplication, etc. - /// - /// - `right`: The right-hand side of the equation. Given the potential - /// complexity of expressions on this side, it is represented as a - /// `proc_macro2::TokenStream` to accommodate any valid Rust expression. - /// - /// # Examples - /// - /// Parsing an equation from macro input: - /// - /// ```rust - /// use macro_tools::exposed::*; - /// let got : tokens::Equation = syn::parse_quote!( default = 31 ); - /// tree_print!( got ); - /// assert_eq!( code_to_str!( got ), "default = 31".to_string() ); - /// ``` - #[ derive( Debug ) ] - pub struct Equation - { - /// The LHS of the equation, represented by a syntactic path. - pub left : syn::Path, - // /// The binary operator (e.g., +, -, *, /) of the equation. - // pub op : syn::BinOp, - /// Equality token. - pub op : syn::Token![ = ], - /// The RHS of the equation, capable of holding complex expressions. - pub right : proc_macro2::TokenStream, - } - - impl syn::parse::Parse for Equation - { - fn parse( input : syn::parse::ParseStream< '_ > ) -> Result< Self > - { - let left : syn::Path = input.parse()?; - let op : syn::Token![ = ] = input.parse()?; - let right : proc_macro2::TokenStream = input.parse()?; - Ok( Equation { left, op, right } ) - } - } - - impl quote::ToTokens for Equation - { - fn to_tokens( &self, tokens : &mut proc_macro2::TokenStream ) - { - self.left.to_tokens( tokens ); - self.op.to_tokens( tokens ); - self.right.to_tokens( tokens ); - } - } - - // impl core::fmt::Display for Equation - // { - // fn fmt( &self, f : &mut core::fmt::Formatter< '_ > ) -> core::fmt::Result - // { - // write!( f, "{}", self.left.to_string() ); - // write!( f, "{}", self.op.to_string() ); - // write!( f, "{}", self.right.to_string() ) - // } - // } - } #[ doc( inline ) ] @@ -181,7 +105,6 @@ pub mod exposed pub use super::private:: { Tokens, - Equation, }; } diff --git a/module/core/macro_tools/src/typ.rs b/module/core/macro_tools/src/typ.rs index 34d45e32b3..609cc717e3 100644 --- a/module/core/macro_tools/src/typ.rs +++ b/module/core/macro_tools/src/typ.rs @@ -5,7 +5,7 @@ /// Internal namespace. pub( crate ) mod private { - use super::super::*; + use crate::*; use interval_adapter::BoundExt; /// Check is the rightmost item of path refering a type is specified type. diff --git a/module/core/macro_tools/src/type_struct.rs b/module/core/macro_tools/src/type_struct.rs deleted file mode 100644 index b3390f5995..0000000000 --- a/module/core/macro_tools/src/type_struct.rs +++ /dev/null @@ -1,215 +0,0 @@ -//! -//! Parse structures, like `struct { a : i32 }`. -//! - -/// Internal namespace. -pub( crate ) mod private -{ - use super::super::*; - // use interval_adapter::BoundExt; - - // xxx : raname to Parsed - - /// Represents the outcome of parsing a Rust `struct` definition. - /// - /// This structure encapsulates details extracted from a structure definition, - /// such as the structure itself, its name, and its fields. It provides a comprehensive - /// view of a parsed structure, facilitating further processing or analysis of its components. - #[ derive( Debug ) ] - pub struct TypeStructParsed - { - /// The parsed structure item, encompassing the entire `struct`. - pub item : syn::ItemStruct, - /// Identifier of the struct, useful for referencing in generated code. - pub item_name : syn::Ident, - /// Collection of struct's fields, including visibility, attributes, and types. - pub fields : syn::Fields, - } - - impl TypeStructParsed - { - - /// Returns a vector of the struct's fields for iteration. - pub fn fields_many( &self ) -> Vec< &syn::Field > - { - match &self.fields - { - syn::Fields::Unnamed( fields ) => fields.unnamed.iter().collect(), - syn::Fields::Named( fields ) => fields.named.iter().collect(), - syn::Fields::Unit => Vec::new(), - } - } - - /// Extracts the types of each field into a vector. - pub fn field_types< 'a >( &'a self ) -> Vec< &'a syn::Type > - { - self.fields_many().iter().map( | field | &field.ty ).collect() - } - - /// Retrieves the names of each field, if they exist. - pub fn field_names( &self ) -> Option< Vec< syn::Ident > > - { - let names: Vec< Option< syn::Ident > > = self.fields_many().iter().map( |field| field.ident.clone() ).collect(); - if names.iter().any( Option::is_none ) - { - None - } - else - { - Some( names.into_iter().filter_map( core::convert::identity ).collect() ) - } - } - - /// Retrieves the type of the first field of the struct. - /// - /// Returns the type if the struct has at least one field, otherwise returns an error. - pub fn first_field_type( &self ) -> Result< syn::Type > - { - let maybe_field = match self.fields - { - syn::Fields::Named( ref fields ) => fields.named.first(), - syn::Fields::Unnamed( ref fields ) => fields.unnamed.first(), - _ => return Err( syn_err!( self.fields.span(), "Expects fields" ) ), - }; - - // let maybe_field = self.fields.0.first(); - // let maybe_field = self.fields; - - if let Some( field ) = maybe_field - { - return Ok( field.ty.clone() ) - } - - return Err( syn_err!( self.item.span(), "Expects type for fields" ) ); - } - - /// Retrieves the name of the first field of the struct, if available. - /// - /// Returns `Some` with the field identifier for named fields, or `None` for unnamed fields. - /// Returns an error if the struct has no fields - pub fn first_field_name( &self ) -> Result< Option< syn::Ident > > - { - let maybe_field = match self.fields - { - syn::Fields::Named( ref fields ) => fields.named.first(), - syn::Fields::Unnamed( ref fields ) => fields.unnamed.first(), - _ => return Err( syn_err!( self.fields.span(), "Expects fields" ) ), - }; - - if let Some( field ) = maybe_field - { - return Ok( field.ident.clone() ) - } - - return Err( syn_err!( self.item.span(), "Expects type for fields" ) ); - } - } - - // - - impl syn::parse::Parse for TypeStructParsed - { - // qqq : write proper documentation with examples of input - - // # example of input - // - // pub struct IsTransparent( bool ); - // - fn parse( input : ParseStream< '_ > ) -> Result< Self > - { - let item : syn::ItemStruct = input.parse()?; - - let item_name = item.ident.clone(); - let fields = item.fields.clone(); - -// let fields_many : Vec< syn::Field > = match item.fields -// { -// syn::Fields::Unnamed( ref fields ) => { fields.unnamed.iter().cloned().collect() }, -// syn::Fields::Named( ref fields ) => { fields.named.iter().cloned().collect() }, -// _ => return Ok( Self { item, item_name, fields, fields_many: Many(vec![]), field_types: vec![], field_names: None } ), -// }; -// -// let fields_many = fields_many.into(); -// let field_types = field_types( &fields_many )?; -// let field_names = field_names( &fields_many )?; -// Ok( Self { item, item_name, fields, fields_many, field_types, field_names } ) - - Ok( Self { item, item_name, fields } ) - } - } - - // - - impl quote::ToTokens for TypeStructParsed - { - fn to_tokens( &self, tokens : &mut proc_macro2::TokenStream ) - { - self.item.to_tokens( tokens ); - } - } - -// fn field_types( fields : &Many< syn::Field > ) -> Result< Vec< syn::Type> > -// { -// let mut field_types : Vec< syn::Type > = vec![]; -// for elem in fields -// { -// field_types.push( elem.ty.clone() ); -// } -// Ok( field_types ) -// } -// -// fn field_names( fields : &Many< syn::Field > ) -> Result< Option< Vec< syn::Ident > > > -// { -// let mut field_names : Vec< syn::Ident > = vec![]; -// for elem in fields -// { -// if let Some( ident ) = &elem.ident -// { -// field_names.push( ident.clone() ); -// } -// else -// { -// return Ok( None ); -// } -// } -// Ok( Some( field_names ) ) -// } - -} - -#[ 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::TypeStructParsed; -} - -/// 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 type_struct; - #[ 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/tests/inc/attr.rs b/module/core/macro_tools/tests/inc/attr.rs deleted file mode 100644 index 6bba6e98fc..0000000000 --- a/module/core/macro_tools/tests/inc/attr.rs +++ /dev/null @@ -1,53 +0,0 @@ - -use super::*; - -// - -#[ test ] -fn parse() -{ - - let attr : syn::Attribute = syn::parse_quote!( #[ default( 31 ) ] ); - tree_print!( attr ); - - let attr : syn::Attribute = syn::parse_quote!( #[ default[ 31 ] ] ); - tree_print!( attr ); - - let attr : syn::Attribute = syn::parse_quote!( #[ former( default = 31 ) ] ); - // tree_print!( attr ); - let got = equation( &attr ).unwrap(); - a_id!( code_to_str!( got ), "default = 31".to_string() ); - a_id!( got.left, syn::parse_quote!( default ) ); - a_id!( got.op, syn::token::Eq::default() ); - a_id!( code_to_str!( got.right ), "31".to_string() ); - -} - -#[ test ] -fn is_standard_standard() -{ - // Test a selection of attributes known to be standard - assert!( is_standard( "cfg" ), "Expected 'cfg' to be a standard attribute." ); - assert!( is_standard( "derive" ), "Expected 'derive' to be a standard attribute." ); - assert!( is_standard( "inline" ), "Expected 'inline' to be a standard attribute." ); - assert!( is_standard( "test" ), "Expected 'test' to be a standard attribute." ); - assert!( is_standard( "doc" ), "Expected 'doc' to be a standard attribute." ); -} - -#[ test ] -fn is_standard_non_standard() -{ - // Test some made-up attributes that should not be standard - assert!( !is_standard( "custom_attr" ), "Expected 'custom_attr' to not be a standard attribute." ); - assert!( !is_standard( "my_attribute" ), "Expected 'my_attribute' to not be a standard attribute." ); - assert!( !is_standard( "special_feature" ), "Expected 'special_feature' to not be a standard attribute." ); -} - -#[ test ] -fn is_standard_edge_cases() -{ - // Test edge cases like empty strings or unusual input - assert!( !is_standard( "" ), "Expected empty string to not be a standard attribute." ); - assert!( !is_standard( " " ), "Expected a single space to not be a standard attribute." ); - assert!( !is_standard( "cfg_attr_extra" ), "Expected 'cfg_attr_extra' to not be a standard attribute." ); -} diff --git a/module/core/macro_tools/tests/inc/attr_prop_test.rs b/module/core/macro_tools/tests/inc/attr_prop_test.rs new file mode 100644 index 0000000000..e2fdb5ecb6 --- /dev/null +++ b/module/core/macro_tools/tests/inc/attr_prop_test.rs @@ -0,0 +1,113 @@ +use super::*; +use quote::ToTokens; + +#[ test ] +fn attr_prop_test() +{ + + #[ derive( Debug, Default, Clone, Copy ) ] + pub struct DebugMarker; + + #[ derive( Debug, Default, Clone, Copy ) ] + pub struct EnabledMarker; + + pub trait AttributePropertyComponent + { + const KEYWORD : &'static str; + } + + impl AttributePropertyComponent for DebugMarker + { + const KEYWORD : &'static str = "debug"; + } + + impl AttributePropertyComponent for EnabledMarker + { + const KEYWORD : &'static str = "enabled"; + } + + #[ derive( Debug, Default ) ] + struct MyAttributes + { + pub debug : AttributePropertyBoolean< DebugMarker >, + pub enabled : AttributePropertyBoolean< EnabledMarker >, + } + + impl syn::parse::Parse for MyAttributes + { + fn parse( input : syn::parse::ParseStream< '_ > ) -> syn::Result< Self > + { + let mut debug = AttributePropertyBoolean::< DebugMarker >::default(); + let mut enabled = AttributePropertyBoolean::< EnabledMarker >::default(); + + while !input.is_empty() + { + let lookahead = input.lookahead1(); + if lookahead.peek( syn::Ident ) + { + let ident : syn::Ident = input.parse()?; + match ident.to_string().as_str() + { + DebugMarker::KEYWORD => debug = input.parse()?, + EnabledMarker::KEYWORD => enabled = input.parse()?, + _ => return Err( lookahead.error() ), + } + } + else + { + return Err( lookahead.error() ); + } + + // Optional comma handling + if input.peek( syn::Token![,] ) + { + input.parse::< syn::Token![,] >()?; + } + } + + Ok( MyAttributes { debug, enabled } ) + } + } + + let input : syn::Attribute = syn::parse_quote!( #[ attribute( enabled = true ) ] ); + let meta = match input.meta + { + syn::Meta::List( meta_list ) => meta_list, + _ => panic!( "Expected a Meta::List" ), + }; + + let nested_meta_stream : proc_macro2::TokenStream = meta.tokens; + let attrs : MyAttributes = syn::parse2( nested_meta_stream ).unwrap(); + println!( "{:?}", attrs ); + + let attr : AttributePropertyBoolean< DebugMarker > = AttributePropertyBoolean::default(); + assert_eq!( attr.internal(), false ); + let attr : AttributePropertyBoolean< DebugMarker > = true.into(); + assert_eq!( attr.internal(), true ); + let attr : AttributePropertyBoolean< DebugMarker > = false.into(); + assert_eq!( attr.internal(), false ); + + let input : syn::Attribute = syn::parse_quote!( #[ attribute( enabled = true ) ] ); + let meta = match input.meta + { + syn::Meta::List( meta_list ) => meta_list, + _ => panic!( "Expected a Meta::List" ), + }; + + let nested_meta_stream : proc_macro2::TokenStream = meta.tokens; + let parsed : MyAttributes = syn::parse2( nested_meta_stream ).unwrap(); + assert_eq!( parsed.enabled.internal(), true ); + assert_eq!( parsed.debug.internal(), false ); + +} + +#[ test ] +fn attribute_property_enabled() +{ + // Test default value + let attr : AttributePropertyOptionalSingletone = Default::default(); + assert_eq!( attr.internal(), None ); + assert_eq!( attr.value( true ), true ); + assert_eq!( attr.value( false ), false ); + +} diff --git a/module/core/macro_tools/tests/inc/attr_test.rs b/module/core/macro_tools/tests/inc/attr_test.rs new file mode 100644 index 0000000000..dab489f65d --- /dev/null +++ b/module/core/macro_tools/tests/inc/attr_test.rs @@ -0,0 +1,76 @@ + +use super::*; + +// + +#[ test ] +fn is_standard_standard() +{ + // Test a selection of attributes known to be standard + assert!( attr::is_standard( "cfg" ), "Expected 'cfg' to be a standard attribute." ); + assert!( attr::is_standard( "derive" ), "Expected 'derive' to be a standard attribute." ); + assert!( attr::is_standard( "inline" ), "Expected 'inline' to be a standard attribute." ); + assert!( attr::is_standard( "test" ), "Expected 'test' to be a standard attribute." ); + assert!( attr::is_standard( "doc" ), "Expected 'doc' to be a standard attribute." ); +} + +#[ test ] +fn is_standard_non_standard() +{ + // Test some made-up attributes that should not be standard + assert!( !attr::is_standard( "custom_attr" ), "Expected 'custom_attr' to not be a standard attribute." ); + assert!( !attr::is_standard( "my_attribute" ), "Expected 'my_attribute' to not be a standard attribute." ); + assert!( !attr::is_standard( "special_feature" ), "Expected 'special_feature' to not be a standard attribute." ); +} + +#[ test ] +fn is_standard_edge_cases() +{ + // Test edge cases like empty strings or unusual input + assert!( !attr::is_standard( "" ), "Expected empty string to not be a standard attribute." ); + assert!( !attr::is_standard( " " ), "Expected a single space to not be a standard attribute." ); + assert!( !attr::is_standard( "cfg_attr_extra" ), "Expected 'cfg_attr_extra' to not be a standard attribute." ); +} + +#[ test ] +fn attribute_component_from_meta() +{ + struct MyComponent; + + impl AttributeComponent for MyComponent + { + const KEYWORD : &'static str = "my_component"; + + fn from_meta( attr : &syn::Attribute ) -> Result< Self > + { + match &attr.meta + { + syn::Meta::NameValue( meta_name_value ) if meta_name_value.path.is_ident( Self::KEYWORD ) => + { + Ok( MyComponent ) + } + _ => Err( syn::Error::new_spanned( attr, "Failed to parse attribute as MyComponent" ) ), + } + } + } + + // Define a sample attribute + let attr : syn::Attribute = syn::parse_quote!( #[ my_component = "value" ] ); + + // Attempt to construct MyComponent from the attribute + let result = MyComponent::from_meta( &attr ); + + // Assert that the construction was successful + assert!( result.is_ok() ); + + // Negative testing + + // Define a sample invalid attribute + let attr : syn::Attribute = syn::parse_quote!( #[ other_component = "value" ] ); + + // Attempt to construct MyComponent from the invalid attribute + let result = MyComponent::from_meta( &attr ); + + // Assert that the construction failed + assert!( result.is_err() ); +} diff --git a/module/core/macro_tools/tests/inc/basic.rs b/module/core/macro_tools/tests/inc/basic.rs deleted file mode 100644 index 7e333ceb64..0000000000 --- a/module/core/macro_tools/tests/inc/basic.rs +++ /dev/null @@ -1,441 +0,0 @@ - -use super::*; - -// - -tests_impls! -{ - - fn tree_diagnostics_str_basic() - { - - let exp = r#"code : std :: collections :: HashMap < i32 , i32 > : -TokenStream [ - Ident { - sym: std, - }, - Punct { - char: ':', - spacing: Joint, - }, - Punct { - char: ':', - spacing: Alone, - }, - Ident { - sym: collections, - }, - Punct { - char: ':', - spacing: Joint, - }, - Punct { - char: ':', - spacing: Alone, - }, - Ident { - sym: HashMap, - }, - Punct { - char: '<', - spacing: Alone, - }, - Ident { - sym: i32, - }, - Punct { - char: ',', - spacing: Alone, - }, - Ident { - sym: i32, - }, - Punct { - char: '>', - spacing: Alone, - }, -]"#; - let code = qt!( std::collections::HashMap< i32, i32 > ); - let got = the_module::tree_diagnostics_str!( code ); - // println!( "{}", got ); - a_id!( got, exp ); - let got = the_module::tree_print!( code ); - // println!( "{}", got ); - a_id!( got, exp ); - - } - - // - - fn syn_err_basic() - { - - // test.case( "basic" ); - let err = the_module::syn_err!( "abc" ); - a_id!( err.to_string(), "abc" ); - - // test.case( "basic, trailing comma" ); - let err = the_module::syn_err!( "abc", ); - a_id!( err.to_string(), "abc" ); - - // test.case( "with span" ); - let code = qt!( core::option::Option< i32 > ); - let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); - let err = the_module::syn_err!( tree_type, "abc" ); - a_id!( err.to_string(), "abc" ); - // a_id!( err.span(), syn::spanned::Spanned::span( &tree_type ) ); - - // test.case( "with span, trailing comma" ); - let code = qt!( core::option::Option< i32 > ); - let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); - let err = the_module::syn_err!( tree_type, "abc", ); - a_id!( err.to_string(), "abc" ); - - // test.case( "with span and args" ); - let code = qt!( core::option::Option< i32 > ); - let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); - let err = the_module::syn_err!( tree_type, "abc{}{}", "def", "ghi" ); - a_id!( err.to_string(), "abcdefghi" ); - // a_id!( err.span(), syn::spanned::Spanned::span( &tree_type ) ); - - // test.case( "with span and args, trailing comma" ); - let code = qt!( core::option::Option< i32 > ); - let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); - let err = the_module::syn_err!( tree_type, "abc{}{}", "def", "ghi", ); - a_id!( err.to_string(), "abcdefghi" ); - - // test.case( "without span" ); - let err = the_module::syn_err!( _, "abc" ); - a_id!( err.to_string(), "abc" ); - - // test.case( "without span, trailing comma" ); - let err = the_module::syn_err!( _, "abc", ); - a_id!( err.to_string(), "abc" ); - - // test.case( "without span, but with args" ); - let err = the_module::syn_err!( _, "abc{}{}", "def", "ghi" ); - a_id!( err.to_string(), "abcdefghi" ); - - // test.case( "without span, trailing comma" ); - let err = the_module::syn_err!( _, "abc{}{}", "def", "ghi", ); - a_id!( err.to_string(), "abcdefghi" ); - - } - - // - - fn type_container_kind_basic() - { - use the_module::exposed::container_kind; - - // test.case( "core::option::Option< i32 >" ); - let code = qt!( core::option::Option< i32 > ); - let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); - let got = container_kind::of_type( &tree_type ); - a_id!( got, the_module::container_kind::ContainerKind::No ); - - // test.case( "core::option::Option< Vec >" ); - let code = qt!( core::option::Option< Vec > ); - let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); - let got = container_kind::of_type( &tree_type ); - a_id!( got, the_module::container_kind::ContainerKind::No ); - - // test.case( "alloc::vec::Vec< i32 >" ); - let code = qt!( alloc::vec::Vec< i32 > ); - let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); - let got = container_kind::of_type( &tree_type ); - a_id!( got, the_module::container_kind::ContainerKind::Vector ); - - // test.case( "alloc::vec::Vec" ); - let code = qt!( alloc::vec::Vec ); - let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); - let got = container_kind::of_type( &tree_type ); - a_id!( got, the_module::container_kind::ContainerKind::Vector ); - - // test.case( "std::vec::Vec< i32 >" ); - let code = qt!( std::vec::Vec< i32 > ); - let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); - let got = container_kind::of_type( &tree_type ); - a_id!( got, the_module::container_kind::ContainerKind::Vector ); - - // test.case( "std::vec::Vec" ); - let code = qt!( std::vec::Vec ); - let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); - let got = container_kind::of_type( &tree_type ); - a_id!( got, the_module::container_kind::ContainerKind::Vector ); - - // test.case( "std::Vec< i32 >" ); - let code = qt!( std::Vec< i32 > ); - let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); - let got = container_kind::of_type( &tree_type ); - a_id!( got, the_module::container_kind::ContainerKind::Vector ); - - // test.case( "std::Vec" ); - let code = qt!( std::Vec ); - let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); - let got = container_kind::of_type( &tree_type ); - a_id!( got, the_module::container_kind::ContainerKind::Vector ); - - // test.case( "not vector" ); - let code = qt!( std::SomeVector< i32, i32 > ); - let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); - let got = container_kind::of_type( &tree_type ); - a_id!( got, the_module::container_kind::ContainerKind::No ); - - // test.case( "hash map" ); - let code = qt!( std::collections::HashMap< i32, i32 > ); - let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); - let got = container_kind::of_type( &tree_type ); - a_id!( got, the_module::container_kind::ContainerKind::HashMap ); - - // test.case( "hash set" ); - let code = qt!( std::collections::HashSet< i32 > ); - let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); - let got = container_kind::of_type( &tree_type ); - a_id!( got, the_module::container_kind::ContainerKind::HashSet ); - - } - - // - - fn type_optional_container_kind_basic() - { - - // test.case( "non optional not container" ); - let code = qt!( i32 ); - let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); - let got = the_module::container_kind::of_optional( &tree_type ); - a_id!( got, ( the_module::container_kind::ContainerKind::No, false ) ); - - // test.case( "optional not container" ); - let code = qt!( core::option::Option< i32 > ); - let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); - let got = the_module::container_kind::of_optional( &tree_type ); - a_id!( got, ( the_module::container_kind::ContainerKind::No, true ) ); - - // test.case( "optional not container" ); - let code = qt!( Option< i32 > ); - let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); - let got = the_module::container_kind::of_optional( &tree_type ); - a_id!( got, ( the_module::container_kind::ContainerKind::No, true ) ); - - - // test.case( "optional vector" ); - let code = qt!( core::option::Option< Vec > ); - let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); - let got = the_module::container_kind::of_optional( &tree_type ); - a_id!( got, ( the_module::container_kind::ContainerKind::Vector, true ) ); - - // test.case( "optional vector" ); - let code = qt!( Option< Vec > ); - let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); - let got = the_module::container_kind::of_optional( &tree_type ); - a_id!( got, ( the_module::container_kind::ContainerKind::Vector, true ) ); - - // test.case( "non optional vector" ); - let code = qt!( std::Vec< i32 > ); - let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); - let got = the_module::container_kind::of_optional( &tree_type ); - a_id!( got, ( the_module::container_kind::ContainerKind::Vector, false ) ); - - - // test.case( "optional vector" ); - let code = qt!( core::option::Option< std::collections::HashMap< i32, i32 > > ); - let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); - let got = the_module::container_kind::of_optional( &tree_type ); - a_id!( got, ( the_module::container_kind::ContainerKind::HashMap, true ) ); - - // test.case( "optional vector" ); - let code = qt!( Option< HashMap > ); - let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); - let got = the_module::container_kind::of_optional( &tree_type ); - a_id!( got, ( the_module::container_kind::ContainerKind::HashMap, true ) ); - - // test.case( "non optional vector" ); - let code = qt!( HashMap< i32, i32 > ); - let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); - let got = the_module::container_kind::of_optional( &tree_type ); - a_id!( got, ( the_module::container_kind::ContainerKind::HashMap, false ) ); - - - // test.case( "optional vector" ); - let code = qt!( core::option::Option< std::collections::HashSet< i32, i32 > > ); - let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); - let got = the_module::container_kind::of_optional( &tree_type ); - a_id!( got, ( the_module::container_kind::ContainerKind::HashSet, true ) ); - - // test.case( "optional vector" ); - let code = qt!( Option< HashSet > ); - let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); - let got = the_module::container_kind::of_optional( &tree_type ); - a_id!( got, ( the_module::container_kind::ContainerKind::HashSet, true ) ); - - // test.case( "non optional vector" ); - let code = qt!( HashSet< i32, i32 > ); - let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); - let got = the_module::container_kind::of_optional( &tree_type ); - a_id!( got, ( the_module::container_kind::ContainerKind::HashSet, false ) ); - - } - - // - - fn type_rightmost_basic() - { - - // test.case( "core::option::Option< i32 >" ); - let code = qt!( core::option::Option< i32 > ); - let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); - let got = the_module::typ::type_rightmost( &tree_type ); - a_id!( got, Some( "Option".to_string() ) ); - - } - - // - - fn type_parameters_basic() - { - - macro_rules! q - { - ( $( $Src : tt )+ ) => - { - syn::parse2::< syn::Type >( qt!( $( $Src )+ ) ).unwrap() - } - } - - // test.case( "core::option::Option< i8, i16, i32, i64 >" ); - let code = qt!( core::option::Option< i8, i16, i32, i64 > ); - let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); - - let got : Vec< syn::Type > = the_module::typ::type_parameters( &tree_type, 0..=0 ).into_iter().cloned().collect(); - let exp = vec![ q!( i8 ) ]; - a_id!( got, exp ); - let got : Vec< syn::Type > = the_module::typ::type_parameters( &tree_type, 0..=1 ).into_iter().cloned().collect(); - let exp = vec![ q!( i8 ), q!( i16 ) ]; - a_id!( got, exp ); - let got : Vec< syn::Type > = the_module::typ::type_parameters( &tree_type, 0..=2 ).into_iter().cloned().collect(); - let exp = vec![ q!( i8 ), q!( i16 ), q!( i32 ) ]; - a_id!( got, exp ); - - let got : Vec< syn::Type > = the_module::typ::type_parameters( &tree_type, 0..0 ).into_iter().cloned().collect(); - let exp : Vec< syn::Type > = vec![]; - a_id!( got, exp ); - let got : Vec< syn::Type > = the_module::typ::type_parameters( &tree_type, 0..1 ).into_iter().cloned().collect(); - let exp = vec![ q!( i8 ) ]; - a_id!( got, exp ); - let got : Vec< syn::Type > = the_module::typ::type_parameters( &tree_type, 0..2 ).into_iter().cloned().collect(); - let exp = vec![ q!( i8 ), q!( i16 ) ]; - a_id!( got, exp ); - - // unbound - let got : Vec< syn::Type > = the_module::typ::type_parameters( &tree_type, .. ).into_iter().cloned().collect(); - let exp = vec![ q!( i8 ), q!( i16 ), q!( i32 ), q!( i64 ) ]; - a_id!( got, exp ); - - let got : Vec< syn::Type > = the_module::typ::type_parameters( &tree_type, .. ).into_iter().cloned().collect(); - let exp = vec![ q!( i8 ), q!( i16 ), q!( i32 ), q!( i64 ) ]; - a_id!( got, exp ); - - let got : Vec< syn::Type > = the_module::typ::type_parameters( &tree_type, .. ).into_iter().cloned().collect(); - let exp = vec![ q!( i8 ), q!( i16 ), q!( i32 ), q!( i64 ) ]; - a_id!( got, exp ); - - } - - // - - // fn equation( attr : &syn::Attribute ) -> Result< ( String, syn::Lit, syn::Meta ), syn::Error > - - // qqq : xxx : fix - // #[test] - // fn attr_pair_single_basic() -> Result< (), syn::Error > - // { - // use syn::spanned::Spanned; - // - // // test.case( "basic" ); - // let input = qt! - // { - // #[ derive( Former ) ] - // pub struct Struct1 - // { - // #[former( default = 31 ) ] - // pub int_1 : i32, - // } - // }; - // - // let ast = match syn::parse2::< syn::DeriveInput >( input ) - // { - // Ok( syntax_tree ) => syntax_tree, - // Err( err ) => return Err( err ), - // }; - // - // let fields = match ast.data - // { - // syn::Data::Struct( ref data_struct ) => match data_struct.fields - // { - // syn::Fields::Named( ref fields_named ) => - // { - // &fields_named.named - // }, - // _ => return Err( syn::Error::new( ast.span(), "Unknown format of data, expected syn::Fields::Named( ref fields_named )" ) ), - // }, - // _ => return Err( syn::Error::new( ast.span(), "Unknown format of data, expected syn::Data::Struct( ref data_struct )" ) ), - // }; - // - // let attr = fields.first().ok_or_else( || err( "No field" ) )?.attrs.first().ok_or_else( || err( "No attr" ) )?; - // - // let ( key, val, meta ) = the_module::equation( &attr )?; - // a_id!( key, "default".to_string() ); - // a_id!( qt!( #val ).to_string(), "31".to_string() ); - // let is = match meta - // { - // syn::Meta::List( _ ) => true, - // _ => false, - // }; - // assert!( is ); - // - // return Ok( () ); - // - // fn err( src : &str ) -> syn::Error - // { - // syn::Error::new( proc_macro2::Span::call_site(), src ) - // } - // } -// -// // -// -// fn path_of() -> Result< (), syn::Error > -// { -// -// let input = qt! -// { -// This::is::path -// }; -// let ast = match syn::parse2::< syn::Path >( input ) -// { -// Ok( syntax_tree ) => syntax_tree, -// Err( err ) => return Err( err ), -// }; -// -// let got = macro_tools::path_of( &ast ); -// a_id!( got, "This::is::path" ); -// -// return Ok( () ); -// } - -} - -// - -tests_index! -{ - tree_diagnostics_str_basic, - syn_err_basic, - type_container_kind_basic, - type_optional_container_kind_basic, - type_rightmost_basic, - type_parameters_basic, - // attr_pair_single_basic, - // path_of, -} diff --git a/module/core/macro_tools/tests/inc/basic_test.rs b/module/core/macro_tools/tests/inc/basic_test.rs new file mode 100644 index 0000000000..78e3dc4460 --- /dev/null +++ b/module/core/macro_tools/tests/inc/basic_test.rs @@ -0,0 +1,14 @@ + +use super::*; + +// + +tests_impls! +{ +} + +// + +tests_index! +{ +} diff --git a/module/core/macro_tools/tests/inc/container_kind_test.rs b/module/core/macro_tools/tests/inc/container_kind_test.rs new file mode 100644 index 0000000000..e6669410a1 --- /dev/null +++ b/module/core/macro_tools/tests/inc/container_kind_test.rs @@ -0,0 +1,160 @@ + +use super::*; + +// + +#[ test ] +fn type_container_kind_basic() +{ + use the_module::exposed::container_kind; + + // test.case( "core::option::Option< i32 >" ); + let code = qt!( core::option::Option< i32 > ); + let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); + let got = container_kind::of_type( &tree_type ); + a_id!( got, the_module::container_kind::ContainerKind::No ); + + // test.case( "core::option::Option< Vec >" ); + let code = qt!( core::option::Option< Vec > ); + let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); + let got = container_kind::of_type( &tree_type ); + a_id!( got, the_module::container_kind::ContainerKind::No ); + + // test.case( "alloc::vec::Vec< i32 >" ); + let code = qt!( alloc::vec::Vec< i32 > ); + let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); + let got = container_kind::of_type( &tree_type ); + a_id!( got, the_module::container_kind::ContainerKind::Vector ); + + // test.case( "alloc::vec::Vec" ); + let code = qt!( alloc::vec::Vec ); + let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); + let got = container_kind::of_type( &tree_type ); + a_id!( got, the_module::container_kind::ContainerKind::Vector ); + + // test.case( "std::vec::Vec< i32 >" ); + let code = qt!( std::vec::Vec< i32 > ); + let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); + let got = container_kind::of_type( &tree_type ); + a_id!( got, the_module::container_kind::ContainerKind::Vector ); + + // test.case( "std::vec::Vec" ); + let code = qt!( std::vec::Vec ); + let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); + let got = container_kind::of_type( &tree_type ); + a_id!( got, the_module::container_kind::ContainerKind::Vector ); + + // test.case( "std::Vec< i32 >" ); + let code = qt!( std::Vec< i32 > ); + let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); + let got = container_kind::of_type( &tree_type ); + a_id!( got, the_module::container_kind::ContainerKind::Vector ); + + // test.case( "std::Vec" ); + let code = qt!( std::Vec ); + let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); + let got = container_kind::of_type( &tree_type ); + a_id!( got, the_module::container_kind::ContainerKind::Vector ); + + // test.case( "not vector" ); + let code = qt!( std::SomeVector< i32, i32 > ); + let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); + let got = container_kind::of_type( &tree_type ); + a_id!( got, the_module::container_kind::ContainerKind::No ); + + // test.case( "hash map" ); + let code = qt!( std::collections::HashMap< i32, i32 > ); + let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); + let got = container_kind::of_type( &tree_type ); + a_id!( got, the_module::container_kind::ContainerKind::HashMap ); + + // test.case( "hash set" ); + let code = qt!( std::collections::HashSet< i32 > ); + let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); + let got = container_kind::of_type( &tree_type ); + a_id!( got, the_module::container_kind::ContainerKind::HashSet ); + +} + +// + +#[ test ] +fn type_optional_container_kind_basic() +{ + + // test.case( "non optional not container" ); + let code = qt!( i32 ); + let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); + let got = the_module::container_kind::of_optional( &tree_type ); + a_id!( got, ( the_module::container_kind::ContainerKind::No, false ) ); + + // test.case( "optional not container" ); + let code = qt!( core::option::Option< i32 > ); + let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); + let got = the_module::container_kind::of_optional( &tree_type ); + a_id!( got, ( the_module::container_kind::ContainerKind::No, true ) ); + + // test.case( "optional not container" ); + let code = qt!( Option< i32 > ); + let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); + let got = the_module::container_kind::of_optional( &tree_type ); + a_id!( got, ( the_module::container_kind::ContainerKind::No, true ) ); + + + // test.case( "optional vector" ); + let code = qt!( core::option::Option< Vec > ); + let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); + let got = the_module::container_kind::of_optional( &tree_type ); + a_id!( got, ( the_module::container_kind::ContainerKind::Vector, true ) ); + + // test.case( "optional vector" ); + let code = qt!( Option< Vec > ); + let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); + let got = the_module::container_kind::of_optional( &tree_type ); + a_id!( got, ( the_module::container_kind::ContainerKind::Vector, true ) ); + + // test.case( "non optional vector" ); + let code = qt!( std::Vec< i32 > ); + let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); + let got = the_module::container_kind::of_optional( &tree_type ); + a_id!( got, ( the_module::container_kind::ContainerKind::Vector, false ) ); + + + // test.case( "optional vector" ); + let code = qt!( core::option::Option< std::collections::HashMap< i32, i32 > > ); + let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); + let got = the_module::container_kind::of_optional( &tree_type ); + a_id!( got, ( the_module::container_kind::ContainerKind::HashMap, true ) ); + + // test.case( "optional vector" ); + let code = qt!( Option< HashMap > ); + let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); + let got = the_module::container_kind::of_optional( &tree_type ); + a_id!( got, ( the_module::container_kind::ContainerKind::HashMap, true ) ); + + // test.case( "non optional vector" ); + let code = qt!( HashMap< i32, i32 > ); + let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); + let got = the_module::container_kind::of_optional( &tree_type ); + a_id!( got, ( the_module::container_kind::ContainerKind::HashMap, false ) ); + + + // test.case( "optional vector" ); + let code = qt!( core::option::Option< std::collections::HashSet< i32, i32 > > ); + let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); + let got = the_module::container_kind::of_optional( &tree_type ); + a_id!( got, ( the_module::container_kind::ContainerKind::HashSet, true ) ); + + // test.case( "optional vector" ); + let code = qt!( Option< HashSet > ); + let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); + let got = the_module::container_kind::of_optional( &tree_type ); + a_id!( got, ( the_module::container_kind::ContainerKind::HashSet, true ) ); + + // test.case( "non optional vector" ); + let code = qt!( HashSet< i32, i32 > ); + let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); + let got = the_module::container_kind::of_optional( &tree_type ); + a_id!( got, ( the_module::container_kind::ContainerKind::HashSet, false ) ); + +} diff --git a/module/core/macro_tools/tests/inc/derive.rs b/module/core/macro_tools/tests/inc/derive_test.rs similarity index 100% rename from module/core/macro_tools/tests/inc/derive.rs rename to module/core/macro_tools/tests/inc/derive_test.rs diff --git a/module/core/macro_tools/tests/inc/diag_test.rs b/module/core/macro_tools/tests/inc/diag_test.rs new file mode 100644 index 0000000000..e39db7d824 --- /dev/null +++ b/module/core/macro_tools/tests/inc/diag_test.rs @@ -0,0 +1,133 @@ + +use super::*; + +// + +tests_impls! +{ + + fn tree_diagnostics_str_basic() + { + + let exp = r#"code : std :: collections :: HashMap < i32 , i32 > : +TokenStream [ + Ident { + sym: std, + }, + Punct { + char: ':', + spacing: Joint, + }, + Punct { + char: ':', + spacing: Alone, + }, + Ident { + sym: collections, + }, + Punct { + char: ':', + spacing: Joint, + }, + Punct { + char: ':', + spacing: Alone, + }, + Ident { + sym: HashMap, + }, + Punct { + char: '<', + spacing: Alone, + }, + Ident { + sym: i32, + }, + Punct { + char: ',', + spacing: Alone, + }, + Ident { + sym: i32, + }, + Punct { + char: '>', + spacing: Alone, + }, +]"#; + let code = qt!( std::collections::HashMap< i32, i32 > ); + let got = the_module::tree_diagnostics_str!( code ); + // println!( "{}", got ); + a_id!( got, exp ); + let got = the_module::tree_print!( code ); + // println!( "{}", got ); + a_id!( got, exp ); + + } + + // + + fn syn_err_basic() + { + + // test.case( "basic" ); + let err = the_module::syn_err!( "abc" ); + a_id!( err.to_string(), "abc" ); + + // test.case( "basic, trailing comma" ); + let err = the_module::syn_err!( "abc", ); + a_id!( err.to_string(), "abc" ); + + // test.case( "with span" ); + let code = qt!( core::option::Option< i32 > ); + let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); + let err = the_module::syn_err!( tree_type, "abc" ); + a_id!( err.to_string(), "abc" ); + // a_id!( err.span(), syn::spanned::Spanned::span( &tree_type ) ); + + // test.case( "with span, trailing comma" ); + let code = qt!( core::option::Option< i32 > ); + let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); + let err = the_module::syn_err!( tree_type, "abc", ); + a_id!( err.to_string(), "abc" ); + + // test.case( "with span and args" ); + let code = qt!( core::option::Option< i32 > ); + let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); + let err = the_module::syn_err!( tree_type, "abc{}{}", "def", "ghi" ); + a_id!( err.to_string(), "abcdefghi" ); + // a_id!( err.span(), syn::spanned::Spanned::span( &tree_type ) ); + + // test.case( "with span and args, trailing comma" ); + let code = qt!( core::option::Option< i32 > ); + let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); + let err = the_module::syn_err!( tree_type, "abc{}{}", "def", "ghi", ); + a_id!( err.to_string(), "abcdefghi" ); + + // test.case( "without span" ); + let err = the_module::syn_err!( _, "abc" ); + a_id!( err.to_string(), "abc" ); + + // test.case( "without span, trailing comma" ); + let err = the_module::syn_err!( _, "abc", ); + a_id!( err.to_string(), "abc" ); + + // test.case( "without span, but with args" ); + let err = the_module::syn_err!( _, "abc{}{}", "def", "ghi" ); + a_id!( err.to_string(), "abcdefghi" ); + + // test.case( "without span, trailing comma" ); + let err = the_module::syn_err!( _, "abc{}{}", "def", "ghi", ); + a_id!( err.to_string(), "abcdefghi" ); + + } + +} + +// + +tests_index! +{ + tree_diagnostics_str_basic, + syn_err_basic, +} diff --git a/module/core/macro_tools/tests/inc/drop_test.rs b/module/core/macro_tools/tests/inc/drop_test.rs new file mode 100644 index 0000000000..8dc0e89c9c --- /dev/null +++ b/module/core/macro_tools/tests/inc/drop_test.rs @@ -0,0 +1,23 @@ + +use super::*; + +#[ test ] +fn test_needs_drop() +{ + struct NeedsDrop; + + impl Drop for NeedsDrop + { + fn drop( &mut self ) {} + } + + assert!( std::mem::needs_drop::< NeedsDrop >() ); + + // Test each of the types with a handwritten TrivialDrop impl above. + assert!( !std::mem::needs_drop::< std::iter::Empty< NeedsDrop > >() ); + assert!( !std::mem::needs_drop::< std::slice::Iter< '_, NeedsDrop > >() ); + assert!( !std::mem::needs_drop::< std::slice::IterMut< '_, NeedsDrop > >() ); + assert!( !std::mem::needs_drop::< std::option::IntoIter< &NeedsDrop > >() ); + assert!( !std::mem::needs_drop::< std::option::IntoIter< &mut NeedsDrop > >() ); + +} diff --git a/module/core/macro_tools/tests/inc/equation_test.rs b/module/core/macro_tools/tests/inc/equation_test.rs new file mode 100644 index 0000000000..735d8261fc --- /dev/null +++ b/module/core/macro_tools/tests/inc/equation_test.rs @@ -0,0 +1,108 @@ + +use super::*; + +// + +tests_impls! +{ + + #[ test ] + fn equation_test() -> Result< () > + { + use syn::spanned::Spanned; + + // test.case( "basic" ); + let input = qt! + { + #[ derive( Former ) ] + pub struct Struct1 + { + #[former( default = 31 ) ] + pub int_1 : i32, + } + }; + + let ast = match syn::parse2::< syn::DeriveInput >( input ) + { + Ok( syntax_tree ) => syntax_tree, + Err( err ) => return Err( err ), + }; + + let fields = match ast.data + { + syn::Data::Struct( ref data_struct ) => match data_struct.fields + { + syn::Fields::Named( ref fields_named ) => + { + &fields_named.named + }, + _ => return Err( syn::Error::new( ast.span(), "Unknown format of data, expected syn::Fields::Named( ref fields_named )" ) ), + }, + _ => return Err( syn::Error::new( ast.span(), "Unknown format of data, expected syn::Data::Struct( ref data_struct )" ) ), + }; + + let attr = fields.first().ok_or_else( || err( "No field" ) )?.attrs.first().ok_or_else( || err( "No attr" ) )?; + + let exp = equation::Equation + { + left : parse_quote!{ default }, + op : parse_quote!{ = }, + right : parse_quote!{ 31 }, + }; + let got = equation::from_meta( &attr )?; + a_id!( got.left, exp.left ); + a_id!( format!( "{:?}", got ), format!( "{:?}", exp ) ); + // a_id!( got.right, exp.right ); + + return Ok( () ); + + fn err( src : &str ) -> syn::Error + { + syn::Error::new( proc_macro2::Span::call_site(), src ) + } + } + + fn equation_parse_test() + { + + let got : the_module::Equation = syn::parse_quote!( default = 31 ); + tree_print!( got ); + a_id!( code_to_str!( got ), "default = 31".to_string() ); + + a_id!( got.left, syn::parse_quote!( default ) ); + a_id!( got.op, syn::token::Eq::default() ); + a_id!( code_to_str!( got.right ), "31".to_string() ); + + } + + fn equation_from_meta_test() + { + + let attr1 : syn::Attribute = syn::parse_quote!( #[ default( 31 ) ] ); + tree_print!( attr1 ); + + let attr1 : syn::Attribute = syn::parse_quote!( #[ default[ 31 ] ] ); + tree_print!( attr1 ); + + let attr1 : syn::Attribute = syn::parse_quote!( #[ former( default = 31 ) ] ); + // tree_print!( attr1 ); + let got = equation::from_meta( &attr1 ).unwrap(); + a_id!( code_to_str!( got ), "default = 31".to_string() ); + a_id!( got.left, syn::parse_quote!( default ) ); + a_id!( got.op, syn::token::Eq::default() ); + a_id!( code_to_str!( got.right ), "31".to_string() ); + + } + +} + +// + +// + +tests_index! +{ + equation_test, + equation_parse_test, + equation_from_meta_test, +} diff --git a/module/core/macro_tools/tests/inc/generic_args.rs b/module/core/macro_tools/tests/inc/generic_args_test.rs similarity index 100% rename from module/core/macro_tools/tests/inc/generic_args.rs rename to module/core/macro_tools/tests/inc/generic_args_test.rs diff --git a/module/core/macro_tools/tests/inc/generic_params.rs b/module/core/macro_tools/tests/inc/generic_params_test.rs similarity index 98% rename from module/core/macro_tools/tests/inc/generic_params.rs rename to module/core/macro_tools/tests/inc/generic_params_test.rs index d9395b74a2..12e82142a9 100644 --- a/module/core/macro_tools/tests/inc/generic_params.rs +++ b/module/core/macro_tools/tests/inc/generic_params_test.rs @@ -112,13 +112,13 @@ fn merge_defaults() // #[ test ] -fn names() +fn only_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() ); + let simplified_generics = macro_tools::generic_params::only_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 @@ -152,7 +152,6 @@ fn decompose_generics_without_where_clause() 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 } diff --git a/module/core/macro_tools/tests/inc/item_struct_test.rs b/module/core/macro_tools/tests/inc/item_struct_test.rs new file mode 100644 index 0000000000..087054cf1e --- /dev/null +++ b/module/core/macro_tools/tests/inc/item_struct_test.rs @@ -0,0 +1,202 @@ + +use super::*; + +#[ test ] +fn field_names_with_named_fields() +{ + use syn::parse_quote; + use the_module::item_struct::field_names; + + let item_struct : syn::ItemStruct = parse_quote! + { + struct Test + { + a : i32, + b : String, + } + }; + + let names = field_names( &item_struct ); + assert!( names.is_some(), "Expected to extract field names" ); + let names : Vec< _ > = names.unwrap().collect(); + assert_eq!( names.len(), 2, "Expected two field names" ); + assert_eq!( names[ 0 ], "a", "First field name mismatch" ); + assert_eq!( names[ 1 ], "b", "Second field name mismatch" ); +} + +#[ test ] +fn field_names_with_unnamed_fields() +{ + use syn::parse_quote; + use the_module::item_struct::field_names; + + let item_struct : syn::ItemStruct = parse_quote! + { + struct Test( i32, String ); + }; + + let names = field_names( &item_struct ); + assert!( names.is_none(), "Expected None for unnamed fields" ); +} + +#[ test ] +fn field_names_with_unit_struct() +{ + use syn::parse_quote; + use the_module::item_struct::field_names; + + let item_struct : syn::ItemStruct = parse_quote! + { + struct Test; + }; + + let names = field_names( &item_struct ); + assert!( names.is_some() ); + let names : Vec< _ > = names.unwrap().collect(); + assert_eq!( names.len(), 0 ); + +} + +#[ test ] +fn field_names_with_reserved_keywords() +{ + use syn::parse_quote; + use the_module::item_struct::field_names; + + let item_struct : syn::ItemStruct = parse_quote! + { + struct Test + { + r#type : i32, + r#fn : String, + } + }; + + let names = field_names( &item_struct ); + assert!( names.is_some(), "Expected to extract field names" ); + let names : Vec< _ > = names.unwrap().collect(); + assert_eq!( names.len(), 2, "Expected two field names" ); + assert_eq!( names[ 0 ], &syn::Ident::new_raw( "type", proc_macro2::Span::call_site() ), "First field name mismatch" ); + assert_eq!( names[ 1 ], &syn::Ident::new_raw( "fn", proc_macro2::Span::call_site() ), "Second field name mismatch" ); + +} + +#[ test ] +fn test_field_or_variant_field() +{ + let input : proc_macro2::TokenStream = quote::quote! + { + struct MyStruct + { + my_field : i32, + } + }; + + let ast : syn::ItemStruct = syn::parse2( input ).unwrap(); + let field = ast.fields.iter().next().unwrap(); + let field_or_variant = the_module::struct_like::FieldOrVariant::from( field ); + + match field_or_variant + { + the_module::struct_like::FieldOrVariant::Field( f ) => + { + assert_eq!( f.ty, syn::parse_quote!( i32 ) ); + }, + _ => panic!( "Expected Field variant" ), + } +} + +#[ test ] +fn test_field_or_variant_variant() +{ + let input : proc_macro2::TokenStream = quote::quote! + { + enum MyEnum + { + Variant1, + } + }; + + let ast : syn::ItemEnum = syn::parse2( input ).unwrap(); + let variant = ast.variants.iter().next().unwrap(); + let field_or_variant = the_module::struct_like::FieldOrVariant::from( variant ); + + match field_or_variant + { + the_module::struct_like::FieldOrVariant::Variant( v ) => + { + let exp : syn::Ident = syn::parse_quote!( Variant1 ); + assert_eq!( v.ident, exp ); + }, + _ => panic!( "Expected Variant variant" ), + } +} + +#[ test ] +fn test_typ() +{ + let input : proc_macro2::TokenStream = quote::quote! + { + struct MyStruct + { + my_field : i32, + } + }; + + let ast : syn::ItemStruct = syn::parse2( input ).unwrap(); + let field = ast.fields.iter().next().unwrap(); + let field_or_variant = the_module::struct_like::FieldOrVariant::from( field ); + assert_eq!( field_or_variant.typ(), Some( &syn::parse_quote!( i32 ) ) ); +} + +#[ test ] +fn test_attrs() +{ + let input : proc_macro2::TokenStream = quote::quote! + { + struct MyStruct + { + #[ some_attr ] + my_field : i32, + } + }; + + let ast : syn::ItemStruct = syn::parse2( input ).unwrap(); + let field = ast.fields.iter().next().unwrap(); + let field_or_variant = the_module::struct_like::FieldOrVariant::from( field ); + assert!( field_or_variant.attrs().iter().any( | attr | attr.path().is_ident( "some_attr" ) ) ); +} + +#[ test ] +fn test_vis() +{ + let input : proc_macro2::TokenStream = quote::quote! + { + struct MyStruct + { + pub my_field : i32, + } + }; + + let ast : syn::ItemStruct = syn::parse2( input ).unwrap(); + let field = ast.fields.iter().next().unwrap(); + let field_or_variant = the_module::struct_like::FieldOrVariant::from( field ); + assert!( matches!( field_or_variant.vis(), Some( syn::Visibility::Public( _ ) ) ) ); +} + +#[ test ] +fn test_ident() +{ + let input : proc_macro2::TokenStream = quote::quote! + { + struct MyStruct + { + my_field : i32, + } + }; + + let ast : syn::ItemStruct = syn::parse2( input ).unwrap(); + let field = ast.fields.iter().next().unwrap(); + let field_or_variant = the_module::struct_like::FieldOrVariant::from( field ); + assert_eq!( field_or_variant.ident().unwrap(), "my_field" ); +} diff --git a/module/core/macro_tools/tests/inc/item.rs b/module/core/macro_tools/tests/inc/item_test.rs similarity index 100% rename from module/core/macro_tools/tests/inc/item.rs rename to module/core/macro_tools/tests/inc/item_test.rs diff --git a/module/core/macro_tools/tests/inc/mod.rs b/module/core/macro_tools/tests/inc/mod.rs index 9bf5c92947..9ed0a80bee 100644 --- a/module/core/macro_tools/tests/inc/mod.rs +++ b/module/core/macro_tools/tests/inc/mod.rs @@ -3,6 +3,8 @@ use super::*; #[ allow( unused_imports ) ] use test_tools::exposed::*; +#[ allow( unused_imports ) ] +use the_module::protected::*; #[ allow( unused_imports ) ] #[ cfg( feature = "enabled" ) ] @@ -11,18 +13,24 @@ mod if_enabled { 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; + mod attr_test; + mod attr_prop_test; + mod basic_test; + mod container_kind_test; + mod derive_test; + mod diag_test; + mod drop_test; + mod equation_test; + mod generic_args_test; + mod generic_params_test; + mod item_test; + mod item_struct_test; + mod phantom_test; + mod quantifier_test; + mod struct_like_test; + mod syntax_test; + mod tokens_test; + mod typ_test; } diff --git a/module/core/macro_tools/tests/inc/phantom.rs b/module/core/macro_tools/tests/inc/phantom_test.rs similarity index 88% rename from module/core/macro_tools/tests/inc/phantom.rs rename to module/core/macro_tools/tests/inc/phantom_test.rs index 990a63d2e7..44c3610e66 100644 --- a/module/core/macro_tools/tests/inc/phantom.rs +++ b/module/core/macro_tools/tests/inc/phantom_test.rs @@ -18,7 +18,7 @@ fn phantom_add_basic() pub struct Struct1< 'a, Context, Formed > { f1 : int32, - _phantom : core::marker::PhantomData< ( &'a(), *const Context, *const Formed ) >, + _phantom : ::core::marker::PhantomData< ( &'a(), *const Context, *const Formed ) >, } }; @@ -64,7 +64,7 @@ fn phantom_add_type_generics() { struct TestStruct< T, U > { - _phantom : core::marker::PhantomData< ( *const T, *const U ) >, + _phantom : ::core::marker::PhantomData< ( *const T, *const U ) >, } }; @@ -86,7 +86,7 @@ fn phantom_add_lifetime_generics() { struct TestStruct< 'a, 'b > { - _phantom : core::marker::PhantomData< ( &'a (), &'b () ) >, + _phantom : ::core::marker::PhantomData< ( &'a (), &'b () ) >, } }; @@ -108,7 +108,7 @@ fn phantom_add_const_generics() { struct TestStruct< const N : usize > { - _phantom : core::marker::PhantomData< ( N, ) >, + _phantom : ::core::marker::PhantomData< ( N, ) >, } }; @@ -130,7 +130,7 @@ fn phantom_add_mixed_generics() { struct TestStruct< T, 'a, const N : usize > { - _phantom : core::marker::PhantomData< ( *const T, &'a (), N ) >, + _phantom : ::core::marker::PhantomData< ( *const T, &'a (), N ) >, } }; @@ -191,7 +191,7 @@ fn phantom_add_unnamed_fields_with_generics() struct TestStruct< T, U > ( T, U, - core::marker::PhantomData< ( *const T, *const U ) >, + ::core::marker::PhantomData< ( *const T, *const U ) >, ); }; @@ -215,7 +215,7 @@ fn phantom_add_unnamed_fields_lifetime_generics() ( &'a i32, &'b f64, - core::marker::PhantomData< ( &'a (), &'b () ) >, + ::core::marker::PhantomData< ( &'a (), &'b () ) >, ); }; @@ -238,7 +238,7 @@ fn phantom_add_unnamed_fields_const_generics() struct TestStruct< const N : usize > ( [ i32 ; N ], - core::marker::PhantomData< ( N, ) >, + ::core::marker::PhantomData< ( N, ) >, ); }; @@ -257,7 +257,7 @@ fn phantom_tuple_empty_generics() let input : Punctuated< GenericParam, Comma > = Punctuated::new(); let result = tuple( &input ); - let exp : syn::Type = parse_quote! { core::marker::PhantomData<()> }; + let exp : syn::Type = parse_quote! { ::core::marker::PhantomData<()> }; let got = result; assert_eq!( format!( "{:?}", exp ), format!( "{:?}", got ), "Expected empty PhantomData, got: {:?}", got ); @@ -274,7 +274,7 @@ fn phantom_tuple_only_type_parameters() 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 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 ); @@ -291,7 +291,7 @@ fn phantom_tuple_mixed_generics() 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 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.rs b/module/core/macro_tools/tests/inc/quantifier_test.rs similarity index 100% rename from module/core/macro_tools/tests/inc/quantifier.rs rename to module/core/macro_tools/tests/inc/quantifier_test.rs diff --git a/module/core/macro_tools/tests/inc/struct_like_test.rs b/module/core/macro_tools/tests/inc/struct_like_test.rs new file mode 100644 index 0000000000..c1de1cf90e --- /dev/null +++ b/module/core/macro_tools/tests/inc/struct_like_test.rs @@ -0,0 +1,432 @@ + +use super::*; + +#[ test ] +fn basic() +{ + use syn::{ parse_quote, ItemStruct }; + use the_module::struct_like; + + // - struct + + let item : ItemStruct = parse_quote! + { + struct Example + { + field1 : i32, + field2 : String + } + }; + let exp = struct_like::StructLike::Struct( item ); + + let got : struct_like::StructLike = parse_quote! + { + struct Example + { + field1 : i32, + field2 : String + } + }; + a_id!( got, exp ); + + // - pub struct + + let item : ItemStruct = parse_quote! + { + pub( crate ) struct Example + { + field1 : i32, + field2 : String + } + }; + let exp = struct_like::StructLike::Struct( item ); + + let got : struct_like::StructLike = parse_quote! + { + pub( crate ) struct Example + { + field1 : i32, + field2 : String + } + }; + a_id!( got, exp ); + + // - enum + + let item : syn::ItemEnum = parse_quote! + { + enum Example + { + field1, + field2( i32 ), + } + }; + let exp = struct_like::StructLike::Enum( item ); + + let got : struct_like::StructLike = parse_quote! + { + enum Example + { + field1, + field2( i32 ), + } + }; + a_id!( got, exp ); + + // - pub enum + + let item : syn::ItemEnum = parse_quote! + { + pub( crate ) enum Example + { + field1, + field2( i32 ), + } + }; + let exp = struct_like::StructLike::Enum( item ); + + let got : struct_like::StructLike = parse_quote! + { + pub( crate ) enum Example + { + field1, + field2( i32 ), + } + }; + a_id!( got, exp ); + + // - unit + + let item : syn::ItemStruct = parse_quote! + { + struct Unit; + }; + let exp = struct_like::StructLike::Unit( item ); + + let got : struct_like::StructLike = parse_quote! + { + struct Unit; + }; + a_id!( got, exp ); + + // - pub unit + + let item : syn::ItemStruct = parse_quote! + { + pub( crate ) struct Unit; + }; + let exp = struct_like::StructLike::Unit( item ); + + let got : struct_like::StructLike = parse_quote! + { + pub( crate ) struct Unit; + }; + a_id!( got, exp ); + +} + +// + +#[ test ] +fn structlike_unit_struct() +{ + use syn::parse_quote; + use the_module::struct_like::StructLike; + + let struct_like : StructLike = parse_quote! + { + struct UnitStruct; + }; + + assert!( matches!( struct_like, StructLike::Unit( _ ) ), "Expected StructLike::Unit variant" ); + assert_eq!( struct_like.ident().to_string(), "UnitStruct", "Struct name mismatch" ); +} + +#[ test ] +fn structlike_struct() +{ + use syn::parse_quote; + use the_module::struct_like::StructLike; + + let struct_like : StructLike = parse_quote! + { + struct RegularStruct + { + a : i32, + b : String, + } + }; + + assert!( matches!( struct_like, StructLike::Struct( _ ) ), "Expected StructLike::Struct variant" ); + assert_eq!( struct_like.ident().to_string(), "RegularStruct", "Struct name mismatch" ); + assert_eq!( struct_like.fields().count(), 2, "Expected two fields" ); +} + +#[ test ] +fn structlike_enum() +{ + use syn::parse_quote; + use the_module::struct_like::StructLike; + + let struct_like : StructLike = parse_quote! + { + enum TestEnum + { + Variant1, + Variant2 { x : i32, y : String }, + } + }; + + assert!( matches!( struct_like, StructLike::Enum( _ ) ), "Expected StructLike::Enum variant" ); + assert_eq!( struct_like.ident().to_string(), "TestEnum", "Enum name mismatch" ); +} + +#[ test ] +fn test_field_or_variant_field() +{ + use syn::parse_quote; + use the_module::struct_like::{ FieldOrVariant, StructLike }; + + let input : StructLike = parse_quote! + { + struct MyStruct + { + my_field : i32, + } + }; + + let field = input.fields().next().expect( "Expected at least one field" ); + let field_or_variant = FieldOrVariant::from( field ); + + match field_or_variant + { + FieldOrVariant::Field( f ) => assert_eq!( f.ty, parse_quote!( i32 ) ), + _ => panic!( "Expected Field variant" ), + } +} + +#[ test ] +fn test_field_or_variant_variant() +{ + use syn::parse_quote; + use the_module::struct_like::{ FieldOrVariant, StructLike }; + + let input : StructLike = parse_quote! + { + enum MyEnum + { + Variant1, + } + }; + + let variant = input.elements().next().expect( "Expected at least one variant" ); + let field_or_variant = FieldOrVariant::from( variant ); + + match field_or_variant + { + FieldOrVariant::Variant( v ) => + { + let exp : syn::Ident = parse_quote!( Variant1 ); + assert_eq!( v.ident, exp ); + }, + _ => panic!( "Expected Variant variant" ), + } +} + +#[ test ] +fn test_typ() +{ + use syn::parse_quote; + use the_module::struct_like::{ FieldOrVariant, StructLike }; + + let input : StructLike = parse_quote! + { + struct MyStruct + { + my_field : i32, + } + }; + + let field = input.fields().next().expect( "Expected at least one field" ); + let field_or_variant = FieldOrVariant::from( field ); + assert_eq!( field_or_variant.typ(), Some( &parse_quote!( i32 ) ) ); +} + +#[ test ] +fn test_attrs() +{ + use syn::parse_quote; + use the_module::struct_like::{ FieldOrVariant, StructLike }; + + let input : StructLike = parse_quote! + { + struct MyStruct + { + #[ some_attr ] + my_field : i32, + } + }; + + let field = input.fields().next().expect( "Expected at least one field" ); + let field_or_variant = FieldOrVariant::from( field ); + assert!( field_or_variant.attrs().iter().any( | attr | attr.path().is_ident( "some_attr" ) ) ); +} + +#[ test ] +fn test_vis() +{ + use syn::parse_quote; + use the_module::struct_like::{ FieldOrVariant, StructLike }; + + let input : StructLike = parse_quote! + { + struct MyStruct + { + pub my_field : i32, + } + }; + + let field = input.fields().next().expect( "Expected at least one field" ); + let field_or_variant = FieldOrVariant::from( field ); + assert!( matches!( field_or_variant.vis(), Some( syn::Visibility::Public( _ ) ) ) ); +} + +#[ test ] +fn test_ident() +{ + use the_module::struct_like::StructLike; + use syn::parse_quote; + use the_module::struct_like::FieldOrVariant; + + let input : StructLike = parse_quote! + { + struct MyStruct + { + my_field : i32, + } + }; + + // Extract the first field using the fields iterator from StructLike + let field = input.fields().next().expect( "Expected at least one field" ); + + let field_or_variant = FieldOrVariant::from( field ); + assert_eq!( field_or_variant.ident().unwrap(), "my_field" ); +} + +// + +#[ test ] +fn struct_with_attrs() +{ + use the_module::struct_like::StructLike; + + let input : proc_macro2::TokenStream = quote::quote! + { + #[ derive( From, InnerFrom, Display, FromStr, PartialEq, Debug ) ] + #[ display( "{a}-{b}" ) ] + pub struct Struct1 + { + a : i32, + b : i32, + } + }; + + let ast : StructLike = syn::parse2( input ).unwrap(); + let field = ast.fields().next().unwrap(); + let field_or_variant = the_module::struct_like::FieldOrVariant::from( field ); + assert_eq!( field_or_variant.ident().unwrap(), "a" ); +} + +// + +// #[ test ] +// fn struct_with_attrs2() +// { +// use the_module::struct_like::StructLike; +// +// let input : proc_macro2::TokenStream = quote::quote! +// { +// #[ derive( Debug, PartialEq, the_module::From ) ] +// #[ debug ] +// pub enum GetData +// { +// #[ allow( dead_code ) ] +// Nothing, +// FromString( String ), +// FromBin( &'static [ u8 ] ), +// } +// }; +// +// let ast : StructLike = syn::parse2( input ).unwrap(); +// let field = ast.elements().next().unwrap(); +// let field_or_variant = the_module::struct_like::FieldOrVariant::from( field ); +// assert_eq!( field_or_variant.ident().unwrap().to_string(), "Nothing" ); +// +// } + +#[ test ] +fn struct_with_attrs2() +{ + use quote::ToTokens; + use the_module::struct_like::{ StructLike, FieldOrVariant }; + + let input : proc_macro2::TokenStream = quote::quote! + { + #[ derive( Debug, PartialEq, the_module::From ) ] + #[ debug ] + pub enum GetData + { + #[ allow( dead_code ) ] + Nothing, + FromString( String ), + FromBin( & 'static [u8] ), + } + }; + + // Parse the input into a StructLike enum + let ast : StructLike = syn::parse2( input ).unwrap(); + + // Ensure the parsed item is an enum + assert!( matches!( ast, StructLike::Enum( _ ) ), "Expected StructLike::Enum variant" ); + + // Check the attributes of the enum + let attrs = ast.attrs(); + assert!( attrs.iter().any( | attr | attr.path().is_ident( "derive" ) ), "Missing derive attribute" ); + assert!( attrs.iter().any( | attr | attr.path().is_ident( "debug" ) ), "Missing debug attribute" ); + + // Check the visibility of the enum + assert!( matches!( ast.vis(), syn::Visibility::Public( _ ) ), "Expected public visibility" ); + + // Check all elements + let elements : Vec< FieldOrVariant< '_ > > = ast.elements().map( FieldOrVariant::from ).collect(); + + // Check the first variant + let first_field_or_variant = &elements[ 0 ]; + assert_eq!( first_field_or_variant.ident().unwrap().to_string(), "Nothing" ); + + // Check the attributes of the first variant + let variant_attrs = first_field_or_variant.attrs(); + assert!( variant_attrs.iter().any( | attr | attr.path().is_ident( "allow" ) ), "Missing allow attribute" ); + + // Check all variant names + let variant_names : Vec< String > = elements.iter().map( | elem | elem.ident().unwrap().to_string() ).collect(); + assert_eq!( variant_names, vec![ "Nothing", "FromString", "FromBin" ], "Variant names do not match" ); + + // Check the types of the variants + let variant_types : Vec< Option< &syn::Type > > = elements.iter().map( | elem | elem.typ() ).collect(); + + // let variant_fields: Vec< syn::Fields > = ast.elements().map( | e | e.fields() ).collect(); + let variant_fields : Vec< syn::Fields > = elements.iter().filter_map( | elem | elem.fields().cloned() ).collect(); + // dbg!( &variant_types ); + + assert_eq!( variant_types.len(), 3, "Expected three variants" ); + assert!( variant_types[ 0 ].is_none(), "First variant should have no type" ); + + assert!( variant_types[ 0 ].is_none() ); + assert!( variant_types[ 1 ].is_none() ); + assert!( variant_types[ 2 ].is_none() ); + + // tree_print!( variant_fields[1] ); + assert_eq!( variant_fields[ 1 ].to_token_stream().to_string(), "(String)", "Second variant should be of type String" ); + assert_eq!( variant_fields[ 2 ].to_token_stream().to_string(), "(& 'static [u8])", "Third variant should be of type & 'static [u8]" ); +} diff --git a/module/core/macro_tools/tests/inc/syntax.rs b/module/core/macro_tools/tests/inc/syntax_test.rs similarity index 100% rename from module/core/macro_tools/tests/inc/syntax.rs rename to module/core/macro_tools/tests/inc/syntax_test.rs diff --git a/module/core/macro_tools/tests/inc/tokens.rs b/module/core/macro_tools/tests/inc/tokens_test.rs similarity index 51% rename from module/core/macro_tools/tests/inc/tokens.rs rename to module/core/macro_tools/tests/inc/tokens_test.rs index aef2707fae..fcae746f5d 100644 --- a/module/core/macro_tools/tests/inc/tokens.rs +++ b/module/core/macro_tools/tests/inc/tokens_test.rs @@ -16,19 +16,3 @@ fn tokens() a_id!( got.to_string(), "# [former (default = 31)]".to_string() ); } - -// - -#[ test ] -fn equation() -{ - - let got : the_module::Equation = syn::parse_quote!( default = 31 ); - tree_print!( got ); - a_id!( code_to_str!( got ), "default = 31".to_string() ); - - a_id!( got.left, syn::parse_quote!( default ) ); - a_id!( got.op, syn::token::Eq::default() ); - a_id!( code_to_str!( got.right ), "31".to_string() ); - -} diff --git a/module/core/macro_tools/tests/inc/typ.rs b/module/core/macro_tools/tests/inc/typ_Test.rs similarity index 63% rename from module/core/macro_tools/tests/inc/typ.rs rename to module/core/macro_tools/tests/inc/typ_Test.rs index 75bd10096d..174c2c243b 100644 --- a/module/core/macro_tools/tests/inc/typ.rs +++ b/module/core/macro_tools/tests/inc/typ_Test.rs @@ -63,7 +63,7 @@ fn is_optional_with_empty_input() assert!( parsed_type_result.is_err(), "Expected parsing to fail for empty input" ); } -// xxx +// #[ test ] fn parameter_first_with_multiple_generics() @@ -126,3 +126,70 @@ fn parameter_first_with_deeply_nested_generics() let expected_type : Type = parse_str( "HashMap< String, Option< i32 > >" ).expect( "Expected type to parse correctly" ); assert_eq!( format!( "{:?}", expected_type ), format!( "{:?}", first_param ), "Extracted type does not match expected" ); } + +// + +#[ test ] +fn type_rightmost_basic() +{ + + // test.case( "core::option::Option< i32 >" ); + let code = qt!( core::option::Option< i32 > ); + let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); + let got = the_module::typ::type_rightmost( &tree_type ); + a_id!( got, Some( "Option".to_string() ) ); + +} + +// + +#[ test ] +fn type_parameters_basic() +{ + + macro_rules! q + { + ( $( $Src : tt )+ ) => + { + syn::parse2::< syn::Type >( qt!( $( $Src )+ ) ).unwrap() + } + } + + // test.case( "core::option::Option< i8, i16, i32, i64 >" ); + let code = qt!( core::option::Option< i8, i16, i32, i64 > ); + let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); + + let got : Vec< syn::Type > = the_module::typ::type_parameters( &tree_type, 0..=0 ).into_iter().cloned().collect(); + let exp = vec![ q!( i8 ) ]; + a_id!( got, exp ); + let got : Vec< syn::Type > = the_module::typ::type_parameters( &tree_type, 0..=1 ).into_iter().cloned().collect(); + let exp = vec![ q!( i8 ), q!( i16 ) ]; + a_id!( got, exp ); + let got : Vec< syn::Type > = the_module::typ::type_parameters( &tree_type, 0..=2 ).into_iter().cloned().collect(); + let exp = vec![ q!( i8 ), q!( i16 ), q!( i32 ) ]; + a_id!( got, exp ); + + let got : Vec< syn::Type > = the_module::typ::type_parameters( &tree_type, 0..0 ).into_iter().cloned().collect(); + let exp : Vec< syn::Type > = vec![]; + a_id!( got, exp ); + let got : Vec< syn::Type > = the_module::typ::type_parameters( &tree_type, 0..1 ).into_iter().cloned().collect(); + let exp = vec![ q!( i8 ) ]; + a_id!( got, exp ); + let got : Vec< syn::Type > = the_module::typ::type_parameters( &tree_type, 0..2 ).into_iter().cloned().collect(); + let exp = vec![ q!( i8 ), q!( i16 ) ]; + a_id!( got, exp ); + + // unbound + let got : Vec< syn::Type > = the_module::typ::type_parameters( &tree_type, .. ).into_iter().cloned().collect(); + let exp = vec![ q!( i8 ), q!( i16 ), q!( i32 ), q!( i64 ) ]; + a_id!( got, exp ); + + let got : Vec< syn::Type > = the_module::typ::type_parameters( &tree_type, .. ).into_iter().cloned().collect(); + let exp = vec![ q!( i8 ), q!( i16 ), q!( i32 ), q!( i64 ) ]; + a_id!( got, exp ); + + let got : Vec< syn::Type > = the_module::typ::type_parameters( &tree_type, .. ).into_iter().cloned().collect(); + let exp = vec![ q!( i8 ), q!( i16 ), q!( i32 ), q!( i64 ) ]; + a_id!( got, exp ); + +} diff --git a/module/core/mem_tools/Readme.md b/module/core/mem_tools/Readme.md index 65fa607810..96f5ae9605 100644 --- a/module/core/mem_tools/Readme.md +++ b/module/core/mem_tools/Readme.md @@ -2,7 +2,7 @@ # Module :: mem_tools - [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/module_mem_tools_push.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/module_mem_tools_push.yml) [![docs.rs](https://img.shields.io/docsrs/mem_tools?color=e3e8f0&logo=docs.rs)](https://docs.rs/mem_tools) [![Open in Gitpod](https://raster.shields.io/static/v1?label=try&message=online&color=eee&logo=gitpod&logoColor=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module/core/mem_tools/examples/mem_tools_trivial.rs,RUN_POSTFIX=--example%20/home/sakapoi/Документи/wTools_fork/module/core/mem_tools/examples/mem_tools_trivial/https://github.com/Wandalen/wTools) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) + [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/module_mem_tools_push.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/module_mem_tools_push.yml) [![docs.rs](https://img.shields.io/docsrs/mem_tools?color=e3e8f0&logo=docs.rs)](https://docs.rs/mem_tools) [![Open in Gitpod](https://raster.shields.io/static/v1?label=try&message=online&color=eee&logo=gitpod&logoColor=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fcore%2Fmem_tools%2Fexamples%2Fmem_tools_trivial.rs,RUN_POSTFIX=--example%20mem_tools_trivial/https://github.com/Wandalen/wTools) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) Collection of tools to manipulate memory. diff --git a/module/core/meta_tools/Readme.md b/module/core/meta_tools/Readme.md index 0cae84d5ad..0d472b069f 100644 --- a/module/core/meta_tools/Readme.md +++ b/module/core/meta_tools/Readme.md @@ -2,7 +2,7 @@ # Module :: meta_tools - [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/module_meta_tools_push.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/module_meta_tools_push.yml) [![docs.rs](https://img.shields.io/docsrs/meta_tools?color=e3e8f0&logo=docs.rs)](https://docs.rs/meta_tools) [![Open in Gitpod](https://raster.shields.io/static/v1?label=try&message=online&color=eee&logo=gitpod&logoColor=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module/core/meta_tools/examples/meta_tools_trivial.rs,RUN_POSTFIX=--example%20/home/sakapoi/Документи/wTools_fork/module/core/meta_tools/examples/meta_tools_trivial/https://github.com/Wandalen/wTools) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) + [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/module_meta_tools_push.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/module_meta_tools_push.yml) [![docs.rs](https://img.shields.io/docsrs/meta_tools?color=e3e8f0&logo=docs.rs)](https://docs.rs/meta_tools) [![Open in Gitpod](https://raster.shields.io/static/v1?label=try&message=online&color=eee&logo=gitpod&logoColor=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fcore%2Fmeta_tools%2Fexamples%2Fmeta_tools_trivial.rs,RUN_POSTFIX=--example%20meta_tools_trivial/https://github.com/Wandalen/wTools) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) Collection of general purpose meta tools. diff --git a/module/core/meta_tools/tests/inc/indents_concat_test.rs b/module/core/meta_tools/tests/inc/indents_concat_test.rs index 51af5f7d3e..58a68bbd5e 100644 --- a/module/core/meta_tools/tests/inc/indents_concat_test.rs +++ b/module/core/meta_tools/tests/inc/indents_concat_test.rs @@ -11,7 +11,7 @@ tests_impls! println!( "MODULES_PATH : {}", env!( "MODULES_PATH" ) ); println!( "WORKSPACE_PATH : {}", env!( "WORKSPACE_PATH" ) ); - // xxx : add to path_tools::{ path::modules(), path::workspace() } + // xxx : add to program_tools::{ path::modules(), path::workspace() } macro_rules! macro1 { diff --git a/module/core/mod_interface/Cargo.toml b/module/core/mod_interface/Cargo.toml index 57abc5e5ab..4876619e96 100644 --- a/module/core/mod_interface/Cargo.toml +++ b/module/core/mod_interface/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mod_interface" -version = "0.18.0" +version = "0.20.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", diff --git a/module/core/mod_interface_meta/Cargo.toml b/module/core/mod_interface_meta/Cargo.toml index 1ccb41735a..b8e13dae71 100644 --- a/module/core/mod_interface_meta/Cargo.toml +++ b/module/core/mod_interface_meta/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mod_interface_meta" -version = "0.18.0" +version = "0.20.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", diff --git a/module/core/mod_interface_meta/src/lib.rs b/module/core/mod_interface_meta/src/lib.rs index 622fcd83d6..eefcd860de 100644 --- a/module/core/mod_interface_meta/src/lib.rs +++ b/module/core/mod_interface_meta/src/lib.rs @@ -35,7 +35,7 @@ // use super::*; // pub mod tokens; // pub mod typ; -// pub mod type_struct; +// pub mod item_struct; // } mod impls; diff --git a/module/core/process_tools/Cargo.toml b/module/core/process_tools/Cargo.toml index 7f31dceaa1..20325bebf8 100644 --- a/module/core/process_tools/Cargo.toml +++ b/module/core/process_tools/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "process_tools" -version = "0.3.0" +version = "0.5.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", @@ -28,22 +28,6 @@ all-features = false [features] default = [ "enabled", "process_environment_is_cicd" ] full = [ "enabled", "process_environment_is_cicd" ] -no_std = [ - "former/no_std", - # xxx - # "proper_path_tools/no_std", - # "error_tools/no_std", - # "iter_tools/no_std", - "test_tools/no_std", -] -use_alloc = [ - "no_std", - "former/use_alloc", - # "proper_path_tools/use_alloc", - # "error_tools/use_alloc", - # "iter_tools/use_alloc", - "test_tools/use_alloc", -] enabled = [ "mod_interface/enabled", "former/enabled", diff --git a/module/core/process_tools/src/process.rs b/module/core/process_tools/src/process.rs index eac9643740..db954da17c 100644 --- a/module/core/process_tools/src/process.rs +++ b/module/core/process_tools/src/process.rs @@ -298,6 +298,7 @@ pub( crate ) mod private } } } + impl core::fmt::Display for Report { fn fmt( &self, f : &mut Formatter< '_ > ) -> core::fmt::Result diff --git a/module/core/program_tools/Cargo.toml b/module/core/program_tools/Cargo.toml new file mode 100644 index 0000000000..30faf2edc3 --- /dev/null +++ b/module/core/program_tools/Cargo.toml @@ -0,0 +1,57 @@ +[package] +name = "program_tools" +version = "0.1.0" +edition = "2021" +authors = [ + "Kostiantyn Wandalen ", +] +license = "MIT" +readme = "Readme.md" +documentation = "https://docs.rs/program_tools" +repository = "https://github.com/Wandalen/wTools/tree/master/module/core/program_tools" +homepage = "https://github.com/Wandalen/wTools/tree/master/module/core/program_tools" +description = """ +Compile and run a Rust program. +""" +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 = [ + "mod_interface/enabled", + "former/enabled", + "proper_path_tools/enabled", + "error_tools/enabled", + "iter_tools/enabled", +] + +[dependencies] +mod_interface = { workspace = true } +former = { workspace = true, features = [ "derive_former" ] } +proper_path_tools = { workspace = true } +error_tools = { workspace = true, features = [ "error_for_app" ] } # qqq : xxx : rid off error_for_app +iter_tools = { workspace = true } + +# ## external +# duct = "0.13.7" + + +[dev-dependencies] +test_tools = { workspace = true } +# assert_fs = { version = "1.1.1" } diff --git a/module/core/program_tools/License b/module/core/program_tools/License new file mode 100644 index 0000000000..6d5ef8559f --- /dev/null +++ b/module/core/program_tools/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/core/program_tools/Readme.md b/module/core/program_tools/Readme.md new file mode 100644 index 0000000000..daf1b1c108 --- /dev/null +++ b/module/core/program_tools/Readme.md @@ -0,0 +1,33 @@ + + +# Module :: program_tools + + [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/module_program_tools_push.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/module_program_tools_push.yml) [![docs.rs](https://img.shields.io/docsrs/program_tools?color=e3e8f0&logo=docs.rs)](https://docs.rs/program_tools) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) + + +Compile and run a Rust program. + + diff --git a/module/core/program_tools/src/lib.rs b/module/core/program_tools/src/lib.rs new file mode 100644 index 0000000000..71c19f2ab4 --- /dev/null +++ b/module/core/program_tools/src/lib.rs @@ -0,0 +1,24 @@ +// #![ 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/program_tools/latest/program_tools/" ) ] +#![ doc = include_str!( concat!( env!( "CARGO_MANIFEST_DIR" ), "/", "Readme.md" ) ) ] + +#![ allow( unused_imports, dead_code, missing_docs ) ] // xxx : rid off + +#[ cfg( feature = "enabled" ) ] +use mod_interface::mod_interface; + +// xxx : move is_cicd here +// println!( "MODULES_PATH : {}", env!( "MODULES_PATH" ) ); +// println!( "WORKSPACE_PATH : {}", env!( "WORKSPACE_PATH" ) ); +// // xxx : add to program_tools::{ path::modules(), path::workspace() } + +#[ cfg( feature = "enabled" ) ] +mod_interface! +{ + + /// Compile and run a Rust program. + layer program; + +} diff --git a/module/core/program_tools/src/program.rs b/module/core/program_tools/src/program.rs new file mode 100644 index 0000000000..5ae707cf30 --- /dev/null +++ b/module/core/program_tools/src/program.rs @@ -0,0 +1,158 @@ +/// Internal namespace. +pub( crate ) mod private +{ + + use former::Former; + use std:: + { + path::{ Path, PathBuf }, + // process::Command, + }; + + // xxx2 : get completed + + #[ derive( Debug, Default, Former ) ] + // #[ debug ] + pub struct Program + { + pub write_path : Option< PathBuf >, + pub read_path : Option< PathBuf >, + #[ subform_entry( name = entry ) ] + pub entries : Vec< Entry >, + #[ subform_entry( name = source ) ] + pub sources : Vec< SourceFile >, + pub cargo_file : Option< CargoFile >, + } + + #[ derive( Debug, Default, Former ) ] + pub struct Plan + { + #[ subform_scalar ] + pub program : Program, + pub calls : Vec< Call >, + } + + #[ derive( Debug, Default ) ] + pub struct Call + { + pub action : Action, + pub current_path : Option< PathBuf >, + pub args : Vec< String >, + pub index_of_entry : i32, + } + + #[ derive( Debug, Default ) ] + pub enum Action + { + #[ default ] + Run, + Build, + Test, + } + + #[ derive( Debug, Default ) ] + pub enum EntryType + { + #[ default ] + Bin, + Lib, + Test, + } + + #[ derive( Debug, Default, Former ) ] + pub struct Entry + { + source_file : SourceFile, + typ : EntryType, + } + + #[ derive( Debug, Default, Former ) ] + pub struct SourceFile + { + file_path : PathBuf, + data : GetData, + } + + #[ derive( Debug, Default, Former ) ] + pub struct CargoFile + { + file_path : PathBuf, + data : GetData, + } + + #[ derive( Debug ) ] + pub enum GetData + { + FromStr( &'static str ), + FromBin( &'static [ u8 ] ), + FromFile( PathBuf ), + FromString( String ), + } + + impl From< &'static str > for GetData + { + #[ inline ] + fn from( src : &'static str ) -> Self + { + Self::FromStr( src ) + } + } + + impl From< &'static [ u8 ] > for GetData + { + #[ inline ] + fn from( src : &'static [ u8 ] ) -> Self + { + Self::FromBin( src ) + } + } + + impl From< PathBuf > for GetData + { + #[ inline ] + fn from( src : PathBuf ) -> Self + { + Self::FromFile( src ) + } + } + + impl From< String > for GetData + { + #[ inline ] + fn from( src : String ) -> Self + { + Self::FromString( src ) + } + } + + impl Default for GetData + { + fn default() -> Self + { + GetData::FromStr( "" ) + } + } + +} + +crate::mod_interface! +{ + + exposed use + { + Program, + }; + + protected use + { + Plan, + Call, + Action, + EntryType, + Entry, + SourceFile, + CargoFile, + GetData, + }; + +} diff --git a/module/core/program_tools/tests/asset/err_out_test/err_out_err.rs b/module/core/program_tools/tests/asset/err_out_test/err_out_err.rs new file mode 100644 index 0000000000..d6bc10ff45 --- /dev/null +++ b/module/core/program_tools/tests/asset/err_out_test/err_out_err.rs @@ -0,0 +1,8 @@ +fn main() +{ + eprintln!( "This is stderr text" ); + + println!( "This is stdout text" ); + + eprintln!( "This is stderr text" ); +} diff --git a/module/core/program_tools/tests/asset/err_out_test/out_err_out.rs b/module/core/program_tools/tests/asset/err_out_test/out_err_out.rs new file mode 100644 index 0000000000..eeb47d28bf --- /dev/null +++ b/module/core/program_tools/tests/asset/err_out_test/out_err_out.rs @@ -0,0 +1,9 @@ +//! need for tests +fn main() +{ + println!( "This is stdout text" ); + + eprintln!( "This is stderr text" ); + + println!( "This is stdout text" ); +} diff --git a/module/core/program_tools/tests/inc/basic.rs b/module/core/program_tools/tests/inc/basic.rs new file mode 100644 index 0000000000..9f9aa8daea --- /dev/null +++ b/module/core/program_tools/tests/inc/basic.rs @@ -0,0 +1,16 @@ +#[ allow( unused_imports ) ] +use super::*; + +#[ test ] +fn basic() +{ + use the_module::program; + + let _plan = program::Plan::former() + .program() + // .source().file_path( "main.rs" ).data( program::GetData::FromStr( "fn main() { println( \"hello!\" ) }" ) ).end() + .source().file_path( "main.rs" ).data( "fn main() { println( \"hello!\" ) }" ).end() + .end() + .end(); + +} diff --git a/module/core/program_tools/tests/inc/mod.rs b/module/core/program_tools/tests/inc/mod.rs new file mode 100644 index 0000000000..d78794e341 --- /dev/null +++ b/module/core/program_tools/tests/inc/mod.rs @@ -0,0 +1,4 @@ +#[ allow( unused_imports ) ] +use super::*; + +mod basic; diff --git a/module/core/program_tools/tests/smoke_test.rs b/module/core/program_tools/tests/smoke_test.rs new file mode 100644 index 0000000000..7fd288e61d --- /dev/null +++ b/module/core/program_tools/tests/smoke_test.rs @@ -0,0 +1,14 @@ + +// #[ cfg( feature = "default" ) ] +#[ test ] +fn local_smoke_test() +{ + ::test_tools::smoke_test_for_local_run(); +} + +// #[ cfg( feature = "default" ) ] +#[ test ] +fn published_smoke_test() +{ + ::test_tools::smoke_test_for_published_run(); +} diff --git a/module/core/program_tools/tests/tests.rs b/module/core/program_tools/tests/tests.rs new file mode 100644 index 0000000000..e353b1d4d9 --- /dev/null +++ b/module/core/program_tools/tests/tests.rs @@ -0,0 +1,11 @@ + +include!( "../../../../module/step/meta/src/module/terminal.rs" ); + +#[ allow( unused_imports ) ] +use program_tools as the_module; + +#[ allow( unused_imports ) ] +use test_tools::exposed::*; + +#[ cfg( feature = "enabled" ) ] +mod inc; diff --git a/module/core/program_tools/tests/tool/asset.rs b/module/core/program_tools/tests/tool/asset.rs new file mode 100644 index 0000000000..7261904225 --- /dev/null +++ b/module/core/program_tools/tests/tool/asset.rs @@ -0,0 +1,140 @@ + +// xxx2 : incorporate the function into a tool + +pub const ASSET_PATH : &str = "tests/asset"; + +macro_rules! ERR_MSG +{ + () + => + { + "Create `.cargo/config.toml` file at root of your project and append it by +``` +[env] +WORKSPACE_PATH = { value = \".\", relative = true } +```" + }; +} + +pub fn path() -> std::io::Result< std::path::PathBuf > +{ + use std:: + { + path::Path, + io::{ self, ErrorKind } + }; + let workspace_path = Path::new( env!( "WORKSPACE_PATH", ERR_MSG!{} ) ); + // dbg!( workspace_path ); + // let crate_path = Path::new( env!( "CARGO_MANIFEST_DIR" ) ); + // dbg!( file!() ); + let dir_path = workspace_path.join( Path::new( file!() ) ); + let dir_path = dir_path.canonicalize()?; + let test_dir = dir_path + .parent() + .ok_or_else( || io::Error::new( ErrorKind::NotFound, format!( "Failed to find parent directory {}", dir_path.display() ) ) )? + .parent() + .ok_or_else( || io::Error::new( ErrorKind::NotFound, format!( "Failed to find parent directory {}", dir_path.display() ) ) )? + .parent() + .ok_or_else( || io::Error::new( ErrorKind::NotFound, format!( "Failed to find parent directory {}", dir_path.display() ) ) )? + ; + // dbg!( &test_dir ); + let assets_path = test_dir.join( Path::new( ASSET_PATH ) ); + // dbg!( &assets_path ); + Ok( assets_path ) +} + +// + +// xxx2 : adjust Former to generate required code easier +// xxx2 : implement the interface + +use former::Former; +use std:: +{ + path::{ Path, PathBuf }, + // process::Command, +}; + +#[ derive( Debug, Default, Former ) ] +pub struct SourceFile +{ + file_path : PathBuf, + data : GetData, +} + +#[ derive( Debug, Default, Former ) ] +pub struct Entry +{ + source_file : SourceFile, + typ : EntryType, +} + +#[ derive( Debug, Default, Former ) ] +pub struct CargoFile +{ + file_path : PathBuf, + data : GetData, +} + +#[ derive( Debug, Default, Former ) ] +// #[ debug ] +pub struct Program +{ + write_path : Option< PathBuf >, + read_path : Option< PathBuf >, + entries : Vec< Entry >, + sources : Vec< SourceFile >, + cargo_file : Option< CargoFile >, +} + +#[ derive( Debug, Default, Former ) ] +pub struct ProgramRun +{ + // #[ embed ] + program : Program, + calls : Vec< ProgramCall >, +} + +#[ derive( Debug ) ] +pub enum GetData +{ + FromStr( &'static str ), + FromBin( &'static [ u8 ] ), + FromFile( PathBuf ), + FromString( String ), +} + +impl Default for GetData +{ + fn default() -> Self + { + GetData::FromStr( "" ) + } +} + +#[ derive( Debug, Default ) ] +pub struct ProgramCall +{ + action : ProgramAction, + current_path : Option< PathBuf >, + args : Vec< String >, + index_of_entry : i32, +} + +#[ derive( Debug, Default ) ] +pub enum ProgramAction +{ + #[ default ] + Run, + Build, + Test, +} + +#[ derive( Debug, Default ) ] +pub enum EntryType +{ + #[ default ] + Bin, + Lib, + Test, +} diff --git a/module/core/proper_path_tools/Cargo.toml b/module/core/proper_path_tools/Cargo.toml index 4c2691e250..7f432561a1 100644 --- a/module/core/proper_path_tools/Cargo.toml +++ b/module/core/proper_path_tools/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "proper_path_tools" -version = "0.4.0" +version = "0.6.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", diff --git a/module/core/proper_path_tools/src/path.rs b/module/core/proper_path_tools/src/path.rs index 7820003f65..7e00e31d19 100644 --- a/module/core/proper_path_tools/src/path.rs +++ b/module/core/proper_path_tools/src/path.rs @@ -2,7 +2,7 @@ pub( crate ) mod private { - #[cfg(feature="no_std")] + #[ cfg( feature = "no_std" ) ] extern crate std; // use std:: @@ -48,46 +48,46 @@ pub( crate ) mod private /// assert_eq!( path::is_glob( "file\\[0-9].txt" ), false ); // Escaped brackets, not a glob pattern /// ``` - pub fn is_glob( path : &str ) -> bool + pub fn is_glob( path : &str ) -> bool { let mut chars = path.chars().peekable(); let mut is_escaped = false; let mut in_brackets = false; let mut in_braces = false; - while let Some( c ) = chars.next() + while let Some( c ) = chars.next() { - if is_escaped + if is_escaped { // If the character is escaped, ignore its special meaning in the next iteration is_escaped = false; continue; } - match c + match c { - '\\' => + '\\' => { is_escaped = !is_escaped; - }, + } '*' | '?' if !in_brackets && !in_braces => return true, - '[' if !in_brackets && !in_braces && !is_escaped => + '[' if !in_brackets && !in_braces && !is_escaped => { // Enter a bracket block, indicating potential glob pattern in_brackets = true; // continue; // Ensure we don't immediately exit on the next char if it's ']' - }, - ']' if in_brackets => + } + ']' if in_brackets => { // in_brackets = false; return true; - }, + } '{' if !in_braces && !is_escaped => in_braces = true, - '}' if in_braces => + '}' if in_braces => { // in_braces = false; return true; - }, + } _ => (), } } @@ -152,48 +152,51 @@ pub( crate ) mod private let mut starts_with_dot = false; let mut iter = path.as_ref().components().peekable(); - if let Some( first ) = iter.peek() + if let Some( first ) = iter.peek() { starts_with_dot = matches!( first, Component::CurDir ); - if matches!( first, Component::RootDir ) + if matches!( first, Component::RootDir ) { components.push( Component::RootDir ); iter.next(); // Skip the root component in further processing } } - for component in iter + for component in iter { match component { - Component::ParentDir => + Component::ParentDir => { - match components.last() + match components.last() { - Some( Component::Normal( _ ) ) => { components.pop(); }, - Some( Component::RootDir ) => + Some( Component::Normal( _ ) ) => + { + components.pop(); + } + Some( Component::RootDir ) => { components.push( Component::ParentDir ); } - Some( Component::ParentDir ) | None => + Some( Component::ParentDir ) | None => { components.push( Component::ParentDir ); - }, + } _ => {} // Do nothing for CurDir } - }, + } Component::CurDir => {} // Skip _ => components.push( component ), } } let mut normalized = PathBuf::new(); - if starts_with_dot || components.is_empty() + if starts_with_dot || components.is_empty() { normalized.push( "." ); } - for component in components.iter() + for component in components.iter() { normalized.push( component.as_os_str() ); } @@ -213,6 +216,10 @@ pub( crate ) mod private { #[ cfg( target_os = "windows" ) ] use std::path::PathBuf; + #[ cfg( feature = "no_std" ) ] + extern crate alloc; + #[ cfg( feature = "no_std" ) ] + use alloc::string::ToString; // println!( "a" ); // let path = path.as_ref().canonicalize()?; @@ -224,15 +231,14 @@ pub( crate ) mod private // // https://github.com/rust-lang/rust/issues/42869 #[ cfg( target_os = "windows" ) ] - let path = + let path = { - const VERBATIM_PREFIX : &str = r#"\\?\"#; + const VERBATIM_PREFIX: &str = r#"\\?\"#; let p = path.display().to_string(); - if p.starts_with( VERBATIM_PREFIX ) + if p.starts_with( VERBATIM_PREFIX ) { - PathBuf::from( &p[ VERBATIM_PREFIX.len() .. ] ) - } - else + PathBuf::from( &p[ VERBATIM_PREFIX.len().. ] ) + } else { path.into() } @@ -285,16 +291,14 @@ pub( crate ) mod private } // Increment and get the current value of the counter safely - let count = COUNTER.with( | counter | + let count = COUNTER.with( | counter | { let val = counter.get(); counter.set( val + 1 ); val - }); + } ); - let timestamp = SystemTime::now() - .duration_since( UNIX_EPOCH )? - .as_nanos(); + let timestamp = SystemTime::now().duration_since( UNIX_EPOCH )?.as_nanos(); let pid = std::process::id(); let tid : String = std::format!( "{:?}", std::thread::current().id() ) @@ -305,6 +309,136 @@ pub( crate ) mod private Ok( std::format!( "{}_{}_{}_{}", timestamp, pid, tid, count ) ) } + /// Joins a list of file system paths into a single absolute path. + /// + /// This function takes a list of file system paths and joins them into a single path, + /// normalizing and simplifying them as it goes. The result is returned as a PathBuf. + /// + /// Examples: + /// + /// ``` + /// + /// let paths = vec![ "a/b/c", "/d/e", "f/g" ]; + /// let joined = proper_path_tools::path::join_paths( paths.into_iter() ); + /// assert_eq!( joined, std::path::PathBuf::from( "/d/e/f/g" ) ); + /// + /// let paths = vec![]; + /// let joined = proper_path_tools::path::join_paths( paths.into_iter() ); + /// assert_eq!( joined, std::path::PathBuf::from( "" ) ); + /// + /// let paths = vec![ "", "a/b", "", "c", "" ]; + /// let joined = proper_path_tools::path::join_paths( paths.into_iter() ); + /// assert_eq!( joined, std::path::PathBuf::from( "/a/b/c" ) ); + /// + /// ``` + pub fn join_paths< 'a, I >( paths : I ) -> std::path::PathBuf + where + I : Iterator< Item = &'a str >, + { + #[ cfg( feature = "no_std" ) ] + extern crate alloc; + #[ cfg( feature = "no_std" ) ] + use alloc::string::String; + #[ cfg( feature = "no_std" ) ] + use alloc::vec::Vec; + + let mut result = String::new(); + + for path in paths { + let mut path = path.replace( '\\', "/" ); + path = path.replace( ':', "" ); + + let mut added_slah = false; + + // If the path is empty, skip it + if path.is_empty() + { + continue; + } + + // If the path starts with '/', clear the result and set it to '/' + if path.starts_with('/') + { + result.clear(); + result.push( '/' ); + } + // If the result doesn't end with '/', append '/' + else if !result.ends_with( '/' ) + { + added_slah = true; + result.push( '/' ); + } + let components: Vec<&str> = path.split( '/' ).collect(); + // Split the path into components + for ( idx, component ) in components.clone().into_iter().enumerate() + { + match component + { + "." => + { + if ( result.ends_with( '/' ) && components.len() > idx + 1 && components[ idx + 1 ].is_empty() ) + || components.len() == idx + 1 + { + result.pop(); + } + } + ".." => + { + if result != "/" + { + if added_slah + { + result.pop(); + added_slah = false; + } + let mut parts : Vec< _ > = result.split( '/' ).collect(); + parts.pop(); + if let Some( part ) = parts.last() + { + if part.is_empty() + { + parts.push( "" ); + } + } + result = parts.join( "/" ); + if result.is_empty() + { + result.push( '/' ); + } + } else + { + result.push_str( &components[ idx.. ].to_vec().join( "/" ) ); + break; + } + } + _ => + { + if !component.is_empty() + { + if result.ends_with( '/' ) + { + result.push_str( component ); + } else + { + result.push( '/' ); + result.push_str( component ); + } + } else if components.len() > idx + 1 && components[ idx + 1 ].is_empty() && path != "/" + { + result.push( '/' ); + } + } + } + } + + if path.ends_with( '/' ) && result != "/" + { + result.push( '/' ); + } + } + + result.into() + } /// Extracts multiple extensions from the given path. /// @@ -356,9 +490,9 @@ pub( crate ) mod private } if let Some( dot_index ) = file_name_str.find( '.' ) { - + let extensions = &file_name_str[ dot_index + 1.. ]; - + return extensions.split( '.' ).map( | s | s.to_string() ).collect() } } @@ -366,10 +500,6 @@ pub( crate ) mod private vec![] } - - - - /// Extracts the parent directory and file stem (without extension) from the given path. /// /// This function takes a path and returns an Option containing the modified path without the extension. @@ -388,7 +518,7 @@ pub( crate ) mod private /// ``` /// use std::path::PathBuf; /// use proper_path_tools::path::without_ext; - /// + /// /// let path = "/path/to/file.txt"; /// let modified_path = without_ext(path); /// assert_eq!(modified_path, Some(PathBuf::from("/path/to/file"))); @@ -397,7 +527,7 @@ pub( crate ) mod private /// ``` /// use std::path::PathBuf; /// use proper_path_tools::path::without_ext; - /// + /// /// let empty_path = ""; /// let modified_path = without_ext(empty_path); /// assert_eq!(modified_path, None); @@ -417,7 +547,7 @@ pub( crate ) mod private } let path_buf = Path::new( path.as_ref() ); - + let parent = match path_buf.parent() { Some( parent ) => parent, @@ -436,17 +566,70 @@ pub( crate ) mod private { String::from( name.to_string_lossy() ) } - + } None => return None, }; let mut full_path = parent.to_path_buf(); full_path.push( file_stem ); - + Some( PathBuf::from( full_path.to_string_lossy().replace( "\\", "/" ) ) ) } + /// Replaces the existing path extension with the provided extension. + /// + /// If the input path is empty or contains non-ASCII characters, or if the provided extension is empty or contains non-ASCII characters, + /// the function returns None. + /// Otherwise, it returns an Option containing the modified path with the new extension. + /// + /// # Arguments + /// + /// * `path` - An object that can be converted into a Path reference, representing the file path. + /// * `ext` - A string slice representing the new extension to be appended to the path. + /// + /// # Returns + /// + /// An Option containing the modified path with the new extension, or None if any of the input parameters are invalid. + /// + /// # Examples + /// + /// ``` + /// use std::path::PathBuf; + /// use proper_path_tools::path::change_ext; + /// + /// let path = "/path/to/file.txt"; + /// let modified_path = change_ext( path, "json" ); + /// assert_eq!( modified_path, Some( PathBuf::from( "/path/to/file.json" ) ) ); + /// ``` + /// + /// ``` + /// use std::path::PathBuf; + /// use proper_path_tools::path::change_ext; + /// + /// let empty_path = ""; + /// let modified_path = change_ext( empty_path, "txt" ); + /// assert_eq!( modified_path, None ); + /// ``` + /// + pub fn change_ext( path : impl AsRef< std::path::Path >, ext : &str ) -> Option< std::path::PathBuf > + { + use std::path::PathBuf; + if path.as_ref().to_string_lossy().is_empty() || !path.as_ref().to_string_lossy().is_ascii() || !ext.is_ascii() + { + return None; + } + + let without_ext = without_ext( path )?; + if ext.is_empty() + { + Some( without_ext ) + } else + { + Some( PathBuf::from( format!( "{}.{}", without_ext.to_string_lossy(), ext ) ) ) + } + } + /// Finds the common directory path among a collection of paths. /// /// Given an iterator of path strings, this function determines the common directory @@ -473,7 +656,7 @@ pub( crate ) mod private /// pub fn path_common< 'a, I >( paths : I ) -> Option< std::string::String > where - I : Iterator< Item = &'a str >, + I: Iterator, { use std::collections::HashMap; #[ cfg( feature = "no_std" ) ] @@ -487,10 +670,10 @@ pub( crate ) mod private { return None; } - + // Create a map to store directory frequencies let mut dir_freqs : HashMap< String, usize > = HashMap::new(); - + let mut paths = orig_paths.clone(); // Iterate over paths to count directory frequencies for path in paths.iter_mut() @@ -499,18 +682,18 @@ pub( crate ) mod private path_remove_double_dots( path ); // Split path into directories let dirs : Vec< &str > = path.split( '/' ).collect(); - + // Iterate over directories for i in 0..dirs.len() { // Construct directory path let mut dir_path = dirs[ 0..i + 1 ].join( "/" ); - + // Increment frequency count *dir_freqs.entry( dir_path.clone() ).or_insert( 0 ) += 1; - + if i != dirs.len() - 1 && !dirs[ i + 1 ].is_empty() { dir_path.push( '/' ); @@ -518,7 +701,7 @@ pub( crate ) mod private } } } - + // Find the directory with the highest frequency let common_dir = dir_freqs .into_iter() @@ -526,9 +709,9 @@ pub( crate ) mod private .map( | ( dir, _ ) | dir ) .max_by_key( | dir | dir.len() ) .unwrap_or_default(); - + let mut result = common_dir.to_string(); - + if result.is_empty() { if orig_paths.iter().any( | path | path.starts_with( '/' ) ) @@ -543,12 +726,12 @@ pub( crate ) mod private { result.push( '.' ); } - + } - + Some( result ) - - + + } /// Removes dot segments (".") from the given path string. @@ -563,20 +746,15 @@ pub( crate ) mod private fn path_remove_dots( path : &mut std::string::String ) { let mut cleaned_parts = vec![]; - for part in path.split( '/' ) { if part == "." { continue; } - cleaned_parts.push( part ); - } - *path = cleaned_parts.join( "/" ); - } /// Removes dot-dot segments ("..") from the given path string. @@ -597,7 +775,6 @@ pub( crate ) mod private let mut cleaned_parts: Vec< &str > = Vec::new(); let mut delete_empty_part = false; - for part in path.split( '/' ) { if part == ".." @@ -608,7 +785,6 @@ pub( crate ) mod private { delete_empty_part = true; } - if pop == ".." { cleaned_parts.push(".."); @@ -636,7 +812,6 @@ pub( crate ) mod private } - /// Rebase the file path relative to a new base path, optionally removing a common prefix. /// /// # Arguments @@ -674,14 +849,12 @@ pub( crate ) mod private /// assert_eq!( rebased_path, PathBuf::from( "/mnt/storage/documents/file.txt" ) ); /// ``` /// - pub fn rebase< T : AsRef< std::path::Path > >( file_path : T, new_path : T, old_path : Option< T > ) -> Option< std::path::PathBuf > + pub fn rebase< T : AsRef< std::path::Path > >( file_path : T, new_path : T, old_path : Option< T > ) -> Option< std::path::PathBuf > { use std::path::Path; use std::path::PathBuf; - let new_path = Path::new( new_path.as_ref() ); let mut main_file_path = Path::new( file_path.as_ref() ); - if old_path.is_some() { let common = path_common( vec![ file_path.as_ref().to_str().unwrap(), old_path.unwrap().as_ref().to_str().unwrap() ].into_iter() )?; @@ -692,11 +865,9 @@ pub( crate ) mod private Err( _ ) => return None, }; } - let mut rebased_path = PathBuf::new(); rebased_path.push( new_path ); rebased_path.push( main_file_path.strip_prefix( "/" ).unwrap_or( main_file_path ) ); - Some( normalize( rebased_path ) ) } @@ -735,22 +906,17 @@ pub( crate ) mod private let mut from = from.as_ref().to_string_lossy().to_string(); let mut to = to.as_ref().to_string_lossy().to_string(); - from = from.replace( ':', "" ); to = to.replace( ':', "" ); - - if from == "./" { from.push_str( &to ); return PathBuf::from( from ) } - if from == "." { return PathBuf::from( to ) } - path_remove_double_dots( &mut from ); path_remove_double_dots( &mut to ); path_remove_dots( &mut from ); @@ -758,18 +924,14 @@ pub( crate ) mod private let mut from_parts: Vec< &str > = from.split( '/' ).collect(); let mut to_parts: Vec< &str > = to.split( '/' ).collect(); - - if from_parts.len() == 1 && from_parts[ 0 ].is_empty() { from_parts.pop(); } - if to_parts.len() == 1 && to_parts[ 0 ].is_empty() { to_parts.pop(); } - let mut common_prefix = 0; for ( idx, ( f, t ) ) in from_parts.iter().zip( to_parts.iter() ).enumerate() { @@ -779,9 +941,7 @@ pub( crate ) mod private } common_prefix = idx + 1; } - let mut result = Vec::new(); - // Add ".." for each directory not in common for i in common_prefix..from_parts.len() { @@ -794,45 +954,34 @@ pub( crate ) mod private { continue; } - result.push( ".." ); } - // Add the remaining directories from 'to' for part in to_parts.iter().skip( common_prefix ) { result.push( *part ); } - // Join the parts into a string let mut relative_path = result.join( "/" ); - - - // If the relative path is empty or the 'to' path is the same as the 'from' path, // set the relative path to "." if relative_path.is_empty() || from == to { relative_path = ".".to_string(); } - if to.ends_with( '/' ) && !relative_path.ends_with( '/' ) && to != "/" { relative_path.push( '/' ); } - - if from.ends_with( '/' ) && to.starts_with( '/' ) && relative_path.starts_with( ".." ) && relative_path != ".." { relative_path.replace_range( ..2 , "." ); } - if from.ends_with( '/' ) && to.starts_with( '/' ) && relative_path == ".." { relative_path = "./..".to_string(); } - PathBuf::from( relative_path ) } @@ -883,26 +1032,22 @@ pub( crate ) mod private return String::new(); } let path_buf = Path::new( path.as_ref() ); - match path_buf.extension() + match path_buf.extension() { - Some( ext ) => - { - ext.to_string_lossy().to_string() - } + Some( ext ) => ext.to_string_lossy().to_string(), None => String::new(), } } - } - -crate::mod_interface! -{ - protected use ext; - protected use exts; +crate::mod_interface! { + protected use ext; + protected use exts; + protected use change_ext; protected use path_relative; protected use rebase; protected use path_common; + protected use join_paths; protected use without_ext; protected use is_glob; protected use normalize; diff --git a/module/core/proper_path_tools/tests/inc/mod.rs b/module/core/proper_path_tools/tests/inc/mod.rs index 4107e22677..31872cad50 100644 --- a/module/core/proper_path_tools/tests/inc/mod.rs +++ b/module/core/proper_path_tools/tests/inc/mod.rs @@ -1,15 +1,18 @@ -#[ allow( unused_imports ) ] +#[allow(unused_imports)] + use super::*; -mod path_normalize; -mod path_is_glob; mod absolute_path; -mod path_exts; -mod path_ext; -mod without_ext; +mod path_change_ext; mod path_common; -mod rebase_path; +mod path_ext; +mod path_exts; +mod path_is_glob; +mod path_join; +mod path_normalize; mod path_relative; +mod rebase_path; +mod without_ext; -#[ cfg( feature = "path_unique_folder_name" ) ] +#[cfg(feature = "path_unique_folder_name")] mod path_unique_folder_name; diff --git a/module/core/proper_path_tools/tests/inc/path_change_ext.rs b/module/core/proper_path_tools/tests/inc/path_change_ext.rs new file mode 100644 index 0000000000..caf19a5c51 --- /dev/null +++ b/module/core/proper_path_tools/tests/inc/path_change_ext.rs @@ -0,0 +1,107 @@ +#[ allow( unused_imports ) ] +use super::*; + + +#[ test ] +fn test_empty_ext() +{ + let got = the_module::path::change_ext( "some.txt", "" ); + let expected = "some"; + assert_eq!( got.unwrap().to_string_lossy(), expected ); +} + +#[ test ] +fn test_simple_change_extension() +{ + let got = the_module::path::change_ext( "some.txt", "json" ); + let expected = "some.json"; + assert_eq!( got.unwrap().to_string_lossy(), expected ); +} + +#[ test ] +fn test_path_with_non_empty_dir_name() +{ + let got = the_module::path::change_ext( "/foo/bar/baz.asdf", "txt" ); + let expected = "/foo/bar/baz.txt"; + assert_eq!( got.unwrap().to_string_lossy(), expected ); +} + +#[ test ] +fn test_change_extension_of_hidden_file() +{ + let got = the_module::path::change_ext( "/foo/bar/.baz", "sh" ); + let expected = "/foo/bar/.baz.sh"; + assert_eq!( got.unwrap().to_string_lossy(), expected ); +} + +#[ test ] +fn test_change_extension_in_composite_file_name() +{ + let got = the_module::path::change_ext( "/foo.coffee.md", "min" ); + let expected = "/foo.coffee.min"; + assert_eq!( got.unwrap().to_string_lossy(), expected ); +} + +#[ test ] +fn test_add_extension_to_file_without_extension() +{ + let got = the_module::path::change_ext( "/foo/bar/baz", "txt" ); + let expected = "/foo/bar/baz.txt"; + assert_eq!( got.unwrap().to_string_lossy(), expected ); +} + +#[ test ] +fn test_path_folder_contains_dot_file_without_extension() +{ + let got = the_module::path::change_ext( "/foo/baz.bar/some.md", "txt" ); + let expected = "/foo/baz.bar/some.txt"; + assert_eq!( got.unwrap().to_string_lossy(), expected ); +} + +#[ test ] +fn test_relative_path_1() +{ + let got = the_module::path::change_ext( "./foo/.baz", "txt" ); + let expected = "./foo/.baz.txt"; + assert_eq!( got.unwrap().to_string_lossy(), expected ); +} + +#[ test ] +fn test_relative_path_2() +{ + let got = the_module::path::change_ext( "./.baz", "txt" ); + let expected = "./.baz.txt"; + assert_eq!( got.unwrap().to_string_lossy(), expected ); +} + +#[ test ] +fn test_relative_path_3() +{ + let got = the_module::path::change_ext( ".baz", "txt" ); + let expected = ".baz.txt"; + assert_eq!( got.unwrap().to_string_lossy(), expected ); +} + +#[ test ] +fn test_relative_path_4() +{ + let got = the_module::path::change_ext( "./baz", "txt" ); + let expected = "./baz.txt"; + assert_eq!( got.unwrap().to_string_lossy(), expected ); +} + +#[ test ] +fn test_relative_path_5() +{ + let got = the_module::path::change_ext( "./foo/baz", "txt" ); + let expected = "./foo/baz.txt"; + assert_eq!( got.unwrap().to_string_lossy(), expected ); +} + +#[ test ] +fn test_relative_path_6() +{ + let got = the_module::path::change_ext( "./foo/", "txt" ); + let expected = "./foo/.txt"; + assert_eq!( got.unwrap().to_string_lossy(), expected ); +} \ No newline at end of file diff --git a/module/core/proper_path_tools/tests/inc/path_join.rs b/module/core/proper_path_tools/tests/inc/path_join.rs new file mode 100644 index 0000000000..4bc1c3db62 --- /dev/null +++ b/module/core/proper_path_tools/tests/inc/path_join.rs @@ -0,0 +1,252 @@ +use super::*; + + +#[ test ] +fn join_empty() +{ + let ( expected, paths ) = ( "", vec![ "" ] ); + let result = the_module::path::join_paths( paths.clone().into_iter() ); + assert_eq!( result, std::path::PathBuf::from( expected ), "Test failed. Paths: '{:?}', Expected: '{}', Got: '{}'", paths, expected, result.to_string_lossy() ); +} + +#[ test ] +fn join_several_empties() +{ + let ( expected, paths ) = ( "", vec![ "", "" ] ); + let result = the_module::path::join_paths( paths.clone().into_iter() ); + assert_eq!( result, std::path::PathBuf::from( expected ), "Test failed. Paths: '{:?}', Expected: '{}', Got: '{}'", paths, expected, result.to_string_lossy() ); +} + + +#[ test ] +fn root_with_absolute() +{ + let ( expected, paths ) = ( "/a/b", vec![ "/", "/a/b" ] ); + let result = the_module::path::join_paths( paths.clone().into_iter() ); + assert_eq!( result, std::path::PathBuf::from( expected ), "Test failed. Paths: '{:?}', Expected: '{}', Got: '{}'", paths, expected, result.to_string_lossy() ); +} + + +#[ test ] +fn root_with_relative() +{ + let ( expected, paths ) = ( "/a/b", vec![ "/", "a/b" ] ); + let result = the_module::path::join_paths( paths.clone().into_iter() ); + assert_eq!( result, std::path::PathBuf::from( expected ), "Test failed. Paths: '{:?}', Expected: '{}', Got: '{}'", paths, expected, result.to_string_lossy() ); +} + + +#[ test ] +fn dir_with_absolute() +{ + let ( expected, paths ) = ( "/a/b", vec![ "/dir", "/a/b" ] ); + let result = the_module::path::join_paths( paths.clone().into_iter() ); + assert_eq!( result, std::path::PathBuf::from( expected ), "Test failed. Paths: '{:?}', Expected: '{}', Got: '{}'", paths, expected, result.to_string_lossy() ); +} + + + +#[ test ] +fn dir_with_relative() +{ + let ( expected, paths ) = ( "/dir/a/b", vec![ "/dir", "a/b" ] ); + let result = the_module::path::join_paths( paths.clone().into_iter() ); + assert_eq!( result, std::path::PathBuf::from( expected ), "Test failed. Paths: '{:?}', Expected: '{}', Got: '{}'", paths, expected, result.to_string_lossy() ); +} + + +#[ test ] +fn trailed_dir_with_absolute() +{ + let ( expected, paths ) = ( "/a/b", vec![ "/dir/", "/a/b" ] ); + let result = the_module::path::join_paths( paths.clone().into_iter() ); + assert_eq!( result, std::path::PathBuf::from( expected ), "Test failed. Paths: '{:?}', Expected: '{}', Got: '{}'", paths, expected, result.to_string_lossy() ); +} + +#[ test ] +fn trailed_dir_with_relative() +{ + let ( expected, paths ) = ( "/dir/a/b", vec![ "/dir/", "a/b" ] ); + let result = the_module::path::join_paths( paths.clone().into_iter() ); + assert_eq!( result, std::path::PathBuf::from( expected ), "Test failed. Paths: '{:?}', Expected: '{}', Got: '{}'", paths, expected, result.to_string_lossy() ); +} + + +#[ test ] +fn dir_with_down() +{ + let ( expected, paths ) = ( "/a/b", vec![ "/dir", "../a/b" ] ); + let result = the_module::path::join_paths( paths.clone().into_iter() ); + assert_eq!( result, std::path::PathBuf::from( expected ), "Test failed. Paths: '{:?}', Expected: '{}', Got: '{}'", paths, expected, result.to_string_lossy() ); +} + + +#[ test ] +fn trailed_dir_with_down() +{ + let ( expected, paths ) = ( "/dir/a/b", vec![ "/dir/", "../a/b" ] ); + let result = the_module::path::join_paths( paths.clone().into_iter() ); + assert_eq!( result, std::path::PathBuf::from( expected ), "Test failed. Paths: '{:?}', Expected: '{}', Got: '{}'", paths, expected, result.to_string_lossy() ); +} + + + +#[ test ] +fn dir_with_several_down() +{ + let ( expected, paths ) = ( "/a/b", vec![ "/dir/dir2", "../../a/b" ] ); + let result = the_module::path::join_paths( paths.clone().into_iter() ); + assert_eq!( result, std::path::PathBuf::from( expected ), "Test failed. Paths: '{:?}', Expected: '{}', Got: '{}'", paths, expected, result.to_string_lossy() ); +} + + +#[ test ] +fn trailed_dir_with_several_down() +{ + let ( expected, paths ) = ( "/a/b", vec![ "/dir/", "../../a/b" ] ); + let result = the_module::path::join_paths( paths.clone().into_iter() ); + assert_eq!( result, std::path::PathBuf::from( expected ), "Test failed. Paths: '{:?}', Expected: '{}', Got: '{}'", paths, expected, result.to_string_lossy() ); +} + + +#[ test ] +fn dir_with_several_down_go_out_of_root() +{ + let ( expected, paths ) = ( "/../a/b", vec![ "/dir", "../../a/b" ] ); + let result = the_module::path::join_paths( paths.clone().into_iter() ); + assert_eq!( result, std::path::PathBuf::from( expected ), "Test failed. Paths: '{:?}', Expected: '{}', Got: '{}'", paths, expected, result.to_string_lossy() ); +} + +#[ test ] +fn trailed_absolute_with_trailed_down() +{ + let ( expected, paths ) = ( "/a/b/", vec![ "/a/b/", "../" ] ); + let result = the_module::path::join_paths( paths.clone().into_iter() ); + assert_eq!( result, std::path::PathBuf::from( expected ), "Test failed. Paths: '{:?}', Expected: '{}', Got: '{}'", paths, expected, result.to_string_lossy() ); +} + + +#[ test ] +fn absolute_with_trailed_down() +{ + let ( expected, paths ) = ( "/a/", vec![ "/a/b", "../" ]) ; + let result = the_module::path::join_paths( paths.clone().into_iter() ); + assert_eq!( result, std::path::PathBuf::from( expected ), "Test failed. Paths: '{:?}', Expected: '{}', Got: '{}'", paths, expected, result.to_string_lossy() ); +} + + +#[ test ] +fn trailed_absolute_with_down() +{ + let ( expected, paths ) = ( "/a/b", vec![ "/a/b/", ".." ] ); + let result = the_module::path::join_paths( paths.clone().into_iter() ); + assert_eq!( result, std::path::PathBuf::from( expected ), "Test failed. Paths: '{:?}', Expected: '{}', Got: '{}'", paths, expected, result.to_string_lossy() ); +} + + +#[ test ] +fn trailed_absolute_with_trailed_here() +{ + let ( expected, paths ) = ( "/a/b/", vec![ "/a/b/", "./" ] ); + let result = the_module::path::join_paths( paths.clone().into_iter() ); + assert_eq!( result, std::path::PathBuf::from( expected ), "Test failed. Paths: '{:?}', Expected: '{}', Got: '{}'", paths, expected, result.to_string_lossy() ); +} + + + +#[ test ] +fn absolute_with_trailed_here() +{ + let ( expected, paths ) = ( "/a/b/", vec![ "/a/b", "./" ] ); + let result = the_module::path::join_paths( paths.clone().into_iter() ); + assert_eq!( result, std::path::PathBuf::from( expected ), "Test failed. Paths: '{:?}', Expected: '{}', Got: '{}'", paths, expected, result.to_string_lossy() ); +} + + +#[ test ] +fn trailed_absolute_with_here() +{ + let ( expected, paths ) = ( "/a/b", vec![ "/a/b/", "." ] ); + let result = the_module::path::join_paths( paths.clone().into_iter() ); + assert_eq!( result, std::path::PathBuf::from( expected ), "Test failed. Paths: '{:?}', Expected: '{}', Got: '{}'", paths, expected, result.to_string_lossy() ); +} + + +#[ test ] +fn join_with_empty() +{ + let ( expected, paths ) = ( "/a/b/c", vec![ "", "a/b", "", "c", "" ] ); + let result = the_module::path::join_paths( paths.clone().into_iter() ); + assert_eq!( result, std::path::PathBuf::from( expected ), "Test failed. Paths: '{:?}', Expected: '{}', Got: '{}'", paths, expected, result.to_string_lossy() ); +} + +#[ test ] +fn join_windows_os_paths() +{ + let ( expected, paths ) = ( "/c/foo/bar/", vec![ "c:\\", "foo\\", "bar\\" ] ); + let result = the_module::path::join_paths( paths.clone().into_iter() ); + assert_eq!( result, std::path::PathBuf::from( expected ), "Test failed. Paths: '{:?}', Expected: '{}', Got: '{}'", paths, expected, result.to_string_lossy() ); +} + + +#[ test ] +fn join_unix_os_paths() +{ + let ( expected, paths ) = ( "/baz/foo", vec![ "/bar/", "/baz", "foo/", "." ] ); + let result = the_module::path::join_paths( paths.clone().into_iter() ); + assert_eq!( result, std::path::PathBuf::from( expected ), "Test failed. Paths: '{:?}', Expected: '{}', Got: '{}'", paths, expected, result.to_string_lossy() ); +} + + +#[ test ] +fn join_unix_os_paths_2() +{ + let ( expected, paths ) = ( "/baz/foo/z", vec![ "/bar/", "/baz", "foo/", ".", "z" ] ); + let result = the_module::path::join_paths( paths.clone().into_iter() ); + assert_eq!( result, std::path::PathBuf::from( expected ), "Test failed. Paths: '{:?}', Expected: '{}', Got: '{}'", paths, expected, result.to_string_lossy() ); +} + + +#[ test ] +fn more_complicated_cases_1() +{ + let ( expected, paths ) = ( "/aa/bb//cc", vec![ "/aa", "bb//", "cc" ] ); + let result = the_module::path::join_paths( paths.clone().into_iter() ); + assert_eq!( result, std::path::PathBuf::from( expected ), "Test failed. Paths: '{:?}', Expected: '{}', Got: '{}'", paths, expected, result.to_string_lossy() ); +} + + + +#[ test ] +fn more_complicated_cases_2() +{ + let ( expected, paths ) = ( "/bb/cc", vec![ "/aa", "/bb", "cc" ] ); + let result = the_module::path::join_paths( paths.clone().into_iter() ); + assert_eq!( result, std::path::PathBuf::from( expected ), "Test failed. Paths: '{:?}', Expected: '{}', Got: '{}'", paths, expected, result.to_string_lossy() ); +} + + +#[ test ] +fn more_complicated_cases_3() +{ + let ( expected, paths ) = ( "//aa/bb//cc//", vec![ "//aa", "bb//", "cc//" ] ); + let result = the_module::path::join_paths( paths.clone().into_iter() ); + assert_eq!( result, std::path::PathBuf::from( expected ), "Test failed. Paths: '{:?}', Expected: '{}', Got: '{}'", paths, expected, result.to_string_lossy() ); +} + + +#[ test ] +fn more_complicated_cases_4() +{ + let ( expected, paths ) = ( "/aa/bb//cc", vec![ "/aa", "bb//", "cc", "." ] ); + let result = the_module::path::join_paths( paths.clone().into_iter() ); + assert_eq!( result, std::path::PathBuf::from( expected ), "Test failed. Paths: '{:?}', Expected: '{}', Got: '{}'", paths, expected, result.to_string_lossy() ); +} + +#[ test ] +fn more_complicated_cases_5() +{ + let ( expected, paths ) = ( "//b//d/..e", vec![ "/", "a", "//b//", "././c", "../d", "..e" ] ); + let result = the_module::path::join_paths( paths.clone().into_iter() ); + assert_eq!( result, std::path::PathBuf::from( expected ), "Test failed. Paths: '{:?}', Expected: '{}', Got: '{}'", paths, expected, result.to_string_lossy() ); +} \ No newline at end of file diff --git a/module/core/reflect_tools/tests/inc/only_test/all.rs b/module/core/reflect_tools/tests/inc/only_test/all.rs index 9708a9f8cf..5fe5831993 100644 --- a/module/core/reflect_tools/tests/inc/only_test/all.rs +++ b/module/core/reflect_tools/tests/inc/only_test/all.rs @@ -7,7 +7,7 @@ fn basic_test() let exp = IsTransparent( true ); a_id!( got, exp ); - // FromInner + // From let got = IsTransparent::from( true ); let exp = IsTransparent( true ); diff --git a/module/core/reflect_tools_meta/src/implementation/reflect.rs b/module/core/reflect_tools_meta/src/implementation/reflect.rs index 9ac4aa1e1c..6016ff192a 100644 --- a/module/core/reflect_tools_meta/src/implementation/reflect.rs +++ b/module/core/reflect_tools_meta/src/implementation/reflect.rs @@ -1,16 +1,26 @@ // use macro_tools::proc_macro2::TokenStream; use super::*; +use macro_tools::{ Result, format_ident, attr, diag }; // pub fn reflect( input : proc_macro::TokenStream ) -> Result< proc_macro2::TokenStream > { - let parsed = syn::parse::< type_struct::TypeStructParsed >( input )?; + let original_input = input.clone(); + let parsed = syn::parse::< syn::ItemStruct >( input )?; + let has_debug = attr::has_debug( parsed.attrs.iter() )?; + let item_name = parsed.ident; let result = qt! { }; + if has_debug + { + let about = format!( "derive : Reflect\nstructure : {item_name}" ); + diag::report_print( about, &original_input, &result ); + } + Ok( result ) } diff --git a/module/core/reflect_tools_meta/src/lib.rs b/module/core/reflect_tools_meta/src/lib.rs index a5e8d1e60c..c2efaadb8a 100644 --- a/module/core/reflect_tools_meta/src/lib.rs +++ b/module/core/reflect_tools_meta/src/lib.rs @@ -34,7 +34,7 @@ use implementation::*; #[ cfg( feature = "enabled" ) ] #[ cfg( feature = "derive_reflect" ) ] -#[ proc_macro_derive( Reflect ) ] +#[ proc_macro_derive( Reflect, attributes( debug ) ) ] pub fn derive_reflect( input : proc_macro::TokenStream ) -> proc_macro::TokenStream { let result = reflect::reflect( input ); diff --git a/module/core/strs_tools/Cargo.toml b/module/core/strs_tools/Cargo.toml index 6f04f509a0..1bd6baf56f 100644 --- a/module/core/strs_tools/Cargo.toml +++ b/module/core/strs_tools/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "strs_tools" -version = "0.11.0" +version = "0.13.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", diff --git a/module/core/strs_tools/Readme.md b/module/core/strs_tools/Readme.md index 8bedfb898d..b070a0bd34 100644 --- a/module/core/strs_tools/Readme.md +++ b/module/core/strs_tools/Readme.md @@ -2,7 +2,7 @@ # Module :: strs_tools - [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/module_strs_tools_push.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/module_strs_tools_push.yml) [![docs.rs](https://img.shields.io/docsrs/strs_tools?color=e3e8f0&logo=docs.rs)](https://docs.rs/strs_tools) [![Open in Gitpod](https://raster.shields.io/static/v1?label=try&message=online&color=eee&logo=gitpod&logoColor=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module/core/strs_tools/examples/strs_tools_trivial.rs,RUN_POSTFIX=--example%20/home/sakapoi/Документи/wTools_fork/module/core/strs_tools/examples/strs_tools_trivial/https://github.com/Wandalen/wTools) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) + [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/module_strs_tools_push.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/module_strs_tools_push.yml) [![docs.rs](https://img.shields.io/docsrs/strs_tools?color=e3e8f0&logo=docs.rs)](https://docs.rs/strs_tools) [![Open in Gitpod](https://raster.shields.io/static/v1?label=try&message=online&color=eee&logo=gitpod&logoColor=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fcore%2Fstrs_tools%2Fexamples%2Fstrs_tools_trivial.rs,RUN_POSTFIX=--example%20strs_tools_trivial/https://github.com/Wandalen/wTools) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) Tools to manipulate strings. diff --git a/module/core/test_tools/Readme.md b/module/core/test_tools/Readme.md index f94ee9a113..e54c622c81 100644 --- a/module/core/test_tools/Readme.md +++ b/module/core/test_tools/Readme.md @@ -2,7 +2,7 @@ # Module :: test_tools - [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/module_test_tools_push.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/module_test_tools_push.yml) [![docs.rs](https://img.shields.io/docsrs/test_tools?color=e3e8f0&logo=docs.rs)](https://docs.rs/test_tools) [![Open in Gitpod](https://raster.shields.io/static/v1?label=try&message=online&color=eee&logo=gitpod&logoColor=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module/core/test_tools/examples/test_tools_trivial.rs,RUN_POSTFIX=--example%20/home/sakapoi/Документи/wTools_fork/module/core/test_tools/examples/test_tools_trivial/https://github.com/Wandalen/wTools) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) + [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/module_test_tools_push.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/module_test_tools_push.yml) [![docs.rs](https://img.shields.io/docsrs/test_tools?color=e3e8f0&logo=docs.rs)](https://docs.rs/test_tools) [![Open in Gitpod](https://raster.shields.io/static/v1?label=try&message=online&color=eee&logo=gitpod&logoColor=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fcore%2Ftest_tools%2Fexamples%2Ftest_tools_trivial.rs,RUN_POSTFIX=--example%20test_tools_trivial/https://github.com/Wandalen/wTools) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) Tools for writing and running tests. diff --git a/module/core/typing_tools/Readme.md b/module/core/typing_tools/Readme.md index 4376dde82c..33bd604df3 100644 --- a/module/core/typing_tools/Readme.md +++ b/module/core/typing_tools/Readme.md @@ -2,7 +2,7 @@ # Module :: typing_tools - [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/module_typing_tools_push.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/module_typing_tools_push.yml) [![docs.rs](https://img.shields.io/docsrs/typing_tools?color=e3e8f0&logo=docs.rs)](https://docs.rs/typing_tools) [![Open in Gitpod](https://raster.shields.io/static/v1?label=try&message=online&color=eee&logo=gitpod&logoColor=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module/core/typing_tools/examples/typing_tools_trivial.rs,RUN_POSTFIX=--example%20/home/sakapoi/Документи/wTools_fork/module/core/typing_tools/examples/typing_tools_trivial/https://github.com/Wandalen/wTools) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) + [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/module_typing_tools_push.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/module_typing_tools_push.yml) [![docs.rs](https://img.shields.io/docsrs/typing_tools?color=e3e8f0&logo=docs.rs)](https://docs.rs/typing_tools) [![Open in Gitpod](https://raster.shields.io/static/v1?label=try&message=online&color=eee&logo=gitpod&logoColor=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fcore%2Ftyping_tools%2Fexamples%2Ftyping_tools_trivial.rs,RUN_POSTFIX=--example%20typing_tools_trivial/https://github.com/Wandalen/wTools) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) Collection of general purpose tools for type checking. diff --git a/module/core/variadic_from/Cargo.toml b/module/core/variadic_from/Cargo.toml index acd3790350..3a8aac6c6e 100644 --- a/module/core/variadic_from/Cargo.toml +++ b/module/core/variadic_from/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "variadic_from" -version = "0.14.0" +version = "0.18.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", diff --git a/module/core/variadic_from/Readme.md b/module/core/variadic_from/Readme.md index 5b0725f5ca..c06be02938 100644 --- a/module/core/variadic_from/Readme.md +++ b/module/core/variadic_from/Readme.md @@ -1,46 +1,145 @@ - - # Module :: variadic_from + - [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/module_variadic_from_push.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/module_variadic_from_push.yml) [![docs.rs](https://img.shields.io/docsrs/variadic_from?color=e3e8f0&logo=docs.rs)](https://docs.rs/variadic_from) [![Open in Gitpod](https://raster.shields.io/static/v1?label=try&message=online&color=eee&logo=gitpod&logoColor=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module/core/variadic_from/examples/variadic_from_trivial.rs,RUN_POSTFIX=--example%20/home/sakapoi/Документи/wTools_fork/module/core/variadic_from/examples/variadic_from_trivial/https://github.com/Wandalen/wTools) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) + [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/module_variadic_from_push.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/module_variadic_from_push.yml) [![docs.rs](https://img.shields.io/docsrs/variadic_from?color=e3e8f0&logo=docs.rs)](https://docs.rs/variadic_from) [![Open in Gitpod](https://raster.shields.io/static/v1?label=try&message=online&color=eee&logo=gitpod&logoColor=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fcore%2Fvariadic_from%2Fexamples%2Fvariadic_from_trivial.rs,RUN_POSTFIX=--example%20variadic_from_trivial/https://github.com/Wandalen/wTools) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) -Variadic from +The variadic from is designed to provide a way to implement the From-like traits for structs with a variable number of fields, allowing them to be constructed from tuples of different lengths or from individual arguments. This functionality is particularly useful for creating flexible constructors that enable different methods of instantiation for a struct. By automating the implementation of traits crate reduces boilerplate code and enhances code readability and maintainability. + +Currently it support up to 3 arguments. If your structure has more than 3 fields derive generates nothing. Also it supports tuple conversion, allowing structs to be instantiated from tuples by leveraging the `From` and `Into` traits for seamless conversion. ### Basic use-case. - + + + + +This example demonstrates the use of the `variadic_from` macro to implement flexible +constructors for a struct, allowing it to be instantiated from different numbers of +arguments or tuples. It also showcases how to derive common traits like `Debug`, +`PartialEq`, `Default`, and `VariadicFrom` for the struct. ```rust -#[ cfg( all(feature = "enabled", feature = "type_variadic_from" ) )] +#[ cfg( not( all(feature = "enabled", feature = "type_variadic_from", feature = "derive_variadic_from" ) ) ) ] +fn main(){} +#[ cfg( all( feature = "enabled", feature = "type_variadic_from", feature = "derive_variadic_from" ) )] fn main() { use variadic_from::exposed::*; - + + // Define a struct `MyStruct` with fields `a` and `b`. + // The struct derives common traits like `Debug`, `PartialEq`, `Default`, and `VariadicFrom`. #[ derive( Debug, PartialEq, Default, VariadicFrom ) ] - struct StructNamedFields + // Use `#[ debug ]` to expand and debug generate code. + // #[ debug ] + struct MyStruct { a : i32, b : i32, } - let got : StructNamedFields = From::from( ( 13, 14 ) ); - let exp = StructNamedFields{ a : 13, b : 14 }; + // Implement the `From1` trait for `MyStruct`, which allows constructing a `MyStruct` instance + // from a single `i32` value by assigning it to both `a` and `b` fields. + + impl From1< i32 > for MyStruct + { + fn from1( a : i32 ) -> Self { Self { a, b : a } } + } + + let got : MyStruct = from!(); + let exp = MyStruct { a : 0, b : 0 }; assert_eq!( got, exp ); - let got : StructNamedFields = from!( 13, 14 ); - let exp = StructNamedFields{ a : 13, b : 14 }; + let got : MyStruct = from!( 13 ); + let exp = MyStruct { a : 13, b : 13 }; assert_eq!( got, exp ); - let got : StructNamedFields = ( 13, 14 ).to(); - let exp = StructNamedFields{ a : 13, b : 14 }; + let got : MyStruct = from!( 13, 14 ); + let exp = MyStruct { a : 13, b : 14 }; assert_eq!( got, exp ); + + dbg!( exp ); + //> MyStruct { + //> a: 13, + //> b: 14, + //> } + } +``` + +
+The code above will be expanded to this +```rust #[ cfg( not( all(feature = "enabled", feature = "type_variadic_from" ) ) ) ] fn main(){} +#[ cfg( all( feature = "enabled", feature = "type_variadic_from" ) )] +fn main() +{ + use variadic_from::exposed::*; + + // Define a struct `MyStruct` with fields `a` and `b`. + // The struct derives common traits like `Debug`, `PartialEq`, `Default` + // `VariadicFrom` defined manually. + #[ derive( Debug, PartialEq, Default ) ] + struct MyStruct + { + a : i32, + b : i32, + } + + // Implement the `From1` trait for `MyStruct`, which allows constructing a `MyStruct` instance + // from a single `i32` value by assigning it to both `a` and `b` fields. + impl From1< i32 > for MyStruct + { + fn from1( a : i32 ) -> Self { Self { a, b : a } } + } + + // == begin of generated + + impl From2< i32, i32 > for MyStruct + { + fn from2( a : i32, b : i32 ) -> Self { Self{ a : a, b : b } } + } + + impl From< ( i32, i32 ) > for MyStruct + { + #[ inline( always ) ] + fn from( ( a, b ) : ( i32, i32 ) ) -> Self + { + Self::from2( a, b ) + } + } + + // == end of generated + + let got : MyStruct = from!(); + let exp = MyStruct { a : 0, b : 0 }; + assert_eq!( got, exp ); + + let got : MyStruct = from!( 13 ); + let exp = MyStruct { a : 13, b : 13 }; + assert_eq!( got, exp ); + + let got : MyStruct = from!( 13, 14 ); + let exp = MyStruct { a : 13, b : 14 }; + assert_eq!( got, exp ); + + dbg!( exp ); + //> MyStruct { + //> a: 13, + //> b: 14, + //> } + +} ``` +
+ +Try out `cargo run --example variadic_from_trivial`. +
+[See code](./examples/variadic_from_trivial.rs). + ### To add to your project ```sh diff --git a/module/core/variadic_from/examples/variadic_from_trivial.rs b/module/core/variadic_from/examples/variadic_from_trivial.rs index d10d259566..a1c68018ec 100644 --- a/module/core/variadic_from/examples/variadic_from_trivial.rs +++ b/module/core/variadic_from/examples/variadic_from_trivial.rs @@ -1,30 +1,52 @@ -//! qqq : write proper description +// variadic_from_trivial.rs -#[ cfg( not( all(feature = "enabled", feature = "type_variadic_from" ) ) ) ] -fn main(){} +//! This example demonstrates the use of the `variadic_from` macro to implement flexible +//! constructors for a struct, allowing it to be instantiated from different numbers of +//! arguments or tuples. It also showcases how to derive common traits like `Debug`, +//! `PartialEq`, `Default`, and `VariadicFrom` for the struct. -#[ cfg( all(feature = "enabled", feature = "type_variadic_from" ) )] +#[ cfg( not( all(feature = "enabled", feature = "type_variadic_from", feature = "derive_variadic_from" ) ) ) ] +fn main(){} +#[ cfg( all( feature = "enabled", feature = "type_variadic_from", feature = "derive_variadic_from" ) )] fn main() { - use variadic_from::*; + use variadic_from::exposed::*; + // Define a struct `MyStruct` with fields `a` and `b`. + // The struct derives common traits like `Debug`, `PartialEq`, `Default`, and `VariadicFrom`. #[ derive( Debug, PartialEq, Default, VariadicFrom ) ] - struct StructNamedFields + // Use `#[ debug ]` to expand and debug generate code. + // #[ debug ] + struct MyStruct { a : i32, b : i32, } - let got : StructNamedFields = From::from( ( 13, 14 ) ); - let exp = StructNamedFields{ a : 13, b : 14 }; + // Implement the `From1` trait for `MyStruct`, which allows constructing a `MyStruct` instance + // from a single `i32` value by assigning it to both `a` and `b` fields. + + impl From1< i32 > for MyStruct + { + fn from1( a : i32 ) -> Self { Self { a, b : a } } + } + + let got : MyStruct = from!(); + let exp = MyStruct { a : 0, b : 0 }; assert_eq!( got, exp ); - let got : StructNamedFields = from!( 13, 14 ); - let exp = StructNamedFields{ a : 13, b : 14 }; + let got : MyStruct = from!( 13 ); + let exp = MyStruct { a : 13, b : 13 }; assert_eq!( got, exp ); - let got : StructNamedFields = ( 13, 14 ).to(); - let exp = StructNamedFields{ a : 13, b : 14 }; + let got : MyStruct = from!( 13, 14 ); + let exp = MyStruct { a : 13, b : 14 }; assert_eq!( got, exp ); -} \ No newline at end of file + dbg!( exp ); + //> MyStruct { + //> a: 13, + //> b: 14, + //> } + +} diff --git a/module/core/variadic_from/examples/variadic_from_trivial_expanded.rs b/module/core/variadic_from/examples/variadic_from_trivial_expanded.rs new file mode 100644 index 0000000000..3f32182add --- /dev/null +++ b/module/core/variadic_from/examples/variadic_from_trivial_expanded.rs @@ -0,0 +1,66 @@ +//! This example demonstrates the use of the `variadic_from` macro to implement flexible +//! constructors for a struct, allowing it to be instantiated from different numbers of +//! arguments or tuples. It also showcases how to derive common traits like `Debug`, +//! `PartialEq`, `Default`, and `VariadicFrom` for the struct. + +#[ cfg( not( all(feature = "enabled", feature = "type_variadic_from" ) ) ) ] +fn main(){} +#[ cfg( all( feature = "enabled", feature = "type_variadic_from" ) )] +fn main() +{ + use variadic_from::exposed::*; + + // Define a struct `MyStruct` with fields `a` and `b`. + // The struct derives common traits like `Debug`, `PartialEq`, `Default` + // `VariadicFrom` defined manually. + #[ derive( Debug, PartialEq, Default ) ] + struct MyStruct + { + a : i32, + b : i32, + } + + // Implement the `From1` trait for `MyStruct`, which allows constructing a `MyStruct` instance + // from a single `i32` value by assigning it to both `a` and `b` fields. + impl From1< i32 > for MyStruct + { + fn from1( a : i32 ) -> Self { Self { a, b : a } } + } + + // == begin of generated + + impl From2< i32, i32 > for MyStruct + { + fn from2( a : i32, b : i32 ) -> Self { Self{ a : a, b : b } } + } + + impl From< ( i32, i32 ) > for MyStruct + { + #[ inline( always ) ] + fn from( ( a, b ) : ( i32, i32 ) ) -> Self + { + Self::from2( a, b ) + } + } + + // == end of generated + + let got : MyStruct = from!(); + let exp = MyStruct { a : 0, b : 0 }; + assert_eq!( got, exp ); + + let got : MyStruct = from!( 13 ); + let exp = MyStruct { a : 13, b : 13 }; + assert_eq!( got, exp ); + + let got : MyStruct = from!( 13, 14 ); + let exp = MyStruct { a : 13, b : 14 }; + assert_eq!( got, exp ); + + dbg!( exp ); + //> MyStruct { + //> a: 13, + //> b: 14, + //> } + +} diff --git a/module/core/variadic_from/src/lib.rs b/module/core/variadic_from/src/lib.rs index 84df5c3381..e05fa1bee2 100644 --- a/module/core/variadic_from/src/lib.rs +++ b/module/core/variadic_from/src/lib.rs @@ -2,21 +2,10 @@ #![ 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/derive_tools/latest/derive_tools/" ) ] -// #![ deny( rust_2018_idioms ) ] -// #![ deny( missing_debug_implementations ) ] -// #![ deny( missing_docs ) ] - -// #![ feature( trait_alias ) ] -// #![ feature( type_name_of_val ) ] - -//! -//! Variadic from. -//! - #![ doc = include_str!( concat!( env!( "CARGO_MANIFEST_DIR" ), "/", "Readme.md" ) ) ] #[ cfg( feature = "enabled" ) ] -pub mod wtools; +pub mod variadic; /// Namespace with dependencies. @@ -25,9 +14,13 @@ pub mod dependency { #[ cfg( derive_variadic_from ) ] pub use ::derive_tools_meta; - } +#[ cfg( feature = "enabled" ) ] +#[ doc( inline ) ] +#[ allow( unused_imports ) ] +pub use protected::*; + /// Protected namespace of the module. #[ cfg( feature = "enabled" ) ] pub mod protected @@ -37,14 +30,9 @@ pub mod protected pub use super::orphan::*; #[ doc( inline ) ] #[ allow( unused_imports ) ] - pub use super::wtools::orphan::*; + pub use super::variadic::orphan::*; } -#[ cfg( feature = "enabled" ) ] -#[ doc( inline ) ] -#[ allow( unused_imports ) ] -pub use protected::*; - /// Orphan namespace of the module. #[ cfg( feature = "enabled" ) ] pub mod orphan @@ -77,9 +65,9 @@ pub mod prelude #[ doc( inline ) ] #[ allow( unused_imports ) ] - pub use super::wtools::prelude::*; - #[ doc( no_inline ) ] - pub use super::wtools; + pub use super::variadic::prelude::*; + // #[ doc( no_inline ) ] + // pub use super::variadic; // #[ doc( no_inline ) ] // pub use ::derive_tools_meta::VariadicFrom; diff --git a/module/core/variadic_from/src/wtools/from.rs b/module/core/variadic_from/src/variadic.rs similarity index 59% rename from module/core/variadic_from/src/wtools/from.rs rename to module/core/variadic_from/src/variadic.rs index c05f43d3e0..ed30e42677 100644 --- a/module/core/variadic_from/src/wtools/from.rs +++ b/module/core/variadic_from/src/variadic.rs @@ -6,84 +6,93 @@ pub( crate ) mod private { +// /// +// /// Constructor without arguments. Alias of Default. +// /// +// +// #[ allow( non_camel_case_types ) ] +// pub trait From_0 +// where +// Self : Sized, +// { +// // /// Constructor without arguments. +// // fn from() -> Self +// // { +// // Self::from_0() +// // } +// /// Constructor without arguments. +// fn from_0() -> Self; +// } +// +// impl< All > From_0 for All +// where +// All : Default, +// { +// /// Constructor without arguments. +// fn from_0() -> Self +// { +// Self::default() +// } +// } + /// - /// Constructor without arguments. Alias of Default. + /// Constructor with single argument. /// #[ allow( non_camel_case_types ) ] - pub trait From_0 + pub trait From1< Arg > where Self : Sized, { - // /// Constructor without arguments. - // fn from() -> Self - // { - // Self::from_0() - // } - /// Constructor without arguments. - fn from_0() -> Self; + /// Constructor with a single arguments. + fn from1( arg : Arg ) -> Self; } - impl< All > From_0 for All + impl< T, All > From1< ( T, ) > for All where - All : Default, + All : From1< T >, { - /// Constructor without arguments. - fn from_0() -> Self + fn from1( arg : ( T, ) ) -> Self { - Self::default() + From1::< T >::from1( arg.0 ) } } - /// - /// Constructor with single argument. - /// - - #[ allow( non_camel_case_types ) ] - pub trait From_1< Arg > + impl< All > From1< () > for All where - Self : Sized, + All : Default, { - // /// Constructor without arguments. - // fn from( arg : Arg ) -> Self - // { - // Self::from_1( arg ) - // } - /// Constructor without arguments. - fn from_1( arg : Arg ) -> Self; + fn from1( _a : () ) -> Self { Self::default() } } - impl< T, All > From_1< ( T, ) > for All - where - All : From_1< T >, - { - fn from_1( arg : ( T, ) ) -> Self - { - From_1::< T >::from_1( arg.0 ) - } - } + // impl< All > From< () > for All + // where + // All : Default, + // { + // fn from( _a : () ) -> Self { Self::default() } + // } - // impl< T, All > From_1< T > for All + // impl< T, All > From1< T > for All // where // All : core::convert::From< T >, // { - // fn from_1( arg : T ) -> Self + // fn from1( arg : T ) -> Self // { // core::convert::From::< T >::from( arg ) // } // } - // impl< T1, T2, All > From_1< ( T1, T2 ) > for All + // impl< T1, T2, All > From1< ( T1, T2 ) > for All // where // All : core::convert::From< ( T1, T2 ) >, // { - // fn from_1( arg : ( T1, T2 ) ) -> Self + // fn from1( arg : ( T1, T2 ) ) -> Self // { // core::convert::From::< ( T1, T2 ) >::from( arg ) // } // } - /// value-to-value conversion that consumes the input value. Change left and rught, but keep semantic of `From_1``. + /// value-to-value conversion that consumes the input value. Change left and rught, but keep semantic of `From1``. #[ allow( non_camel_case_types ) ] pub trait Into1< T > : Sized { @@ -93,30 +102,30 @@ pub( crate ) mod private impl< All, F > Into1< F > for All where - F : From_1< All >, + F : From1< All >, { #[ inline ] fn to( self ) -> F { - F::from_1( self ) + F::from1( self ) } } // impl< All, F > Into1< F > for All // where - // F : From_1< F >, + // F : From1< F >, // F : From< All >, // { // #[ inline ] // fn to( self ) -> F // { - // F::from_1( From::from( self ) ) + // F::from1( From::from( self ) ) // } // } // impl< T, All > From< ( T, ) > for All // where - // All : From_1< T >, + // All : From1< T >, // { // } @@ -125,26 +134,26 @@ pub( crate ) mod private /// #[ allow( non_camel_case_types ) ] - pub trait From_2< Arg1, Arg2 > + pub trait From2< Arg1, Arg2 > where Self : Sized, { // /// Constructor with two arguments. // fn from( arg1 : Arg1, arg2 : Arg2 ) -> Self // { - // Self::from_2( arg1, arg2 ) + // Self::from2( arg1, arg2 ) // } /// Constructor with two arguments. - fn from_2( arg1 : Arg1, arg2 : Arg2 ) -> Self; + fn from2( arg1 : Arg1, arg2 : Arg2 ) -> Self; } - impl< T1, T2, All > From_1< ( T1, T2 ) > for All + impl< T1, T2, All > From1< ( T1, T2 ) > for All where - All : From_2< T1, T2 >, + All : From2< T1, T2 >, { - fn from_1( arg : ( T1, T2 ) ) -> Self + fn from1( arg : ( T1, T2 ) ) -> Self { - From_2::< T1, T2 >::from_2( arg.0, arg.1 ) + From2::< T1, T2 >::from2( arg.0, arg.1 ) } } @@ -153,26 +162,26 @@ pub( crate ) mod private /// #[ allow( non_camel_case_types ) ] - pub trait From_3< Arg1, Arg2, Arg3 > + pub trait From3< Arg1, Arg2, Arg3 > where Self : Sized, { // /// Constructor with three arguments. // fn from( arg1 : Arg1, arg2 : Arg2, arg3 : Arg3 ) -> Self // { - // Self::from_3( arg1, arg2, arg3 ) + // Self::from3( arg1, arg2, arg3 ) // } /// Constructor with three arguments. - fn from_3( arg1 : Arg1, arg2 : Arg2, arg3 : Arg3 ) -> Self; + fn from3( arg1 : Arg1, arg2 : Arg2, arg3 : Arg3 ) -> Self; } - impl< T1, T2, T3, All > From_1< ( T1, T2, T3 ) > for All + impl< T1, T2, T3, All > From1< ( T1, T2, T3 ) > for All where - All : From_3< T1, T2, T3 >, + All : From3< T1, T2, T3 >, { - fn from_1( arg : ( T1, T2, T3 ) ) -> Self + fn from1( arg : ( T1, T2, T3 ) ) -> Self { - From_3::< T1, T2, T3 >::from_3( arg.0, arg.1, arg.2 ) + From3::< T1, T2, T3 >::from3( arg.0, arg.1, arg.2 ) } } @@ -181,32 +190,58 @@ pub( crate ) mod private // /// // // #[ allow( non_camel_case_types ) ] -// pub trait From_4< Arg1, Arg2, Arg3, Arg4 > +// pub trait From4< Arg1, Arg2, Arg3, Arg4 > // where // Self : Sized, // { // /// Constructor with four arguments. // fn from( arg1 : Arg1, arg2 : Arg2, arg3 : Arg3, arg4 : Arg4 ) -> Self // { -// Self::from_4( arg1, arg2, arg3, arg4 ) +// Self::from4( arg1, arg2, arg3, arg4 ) // } // /// Constructor with four arguments. -// fn from_4( arg1 : Arg1, arg2 : Arg2, arg3 : Arg3, arg4 : Arg4 ) -> Self; +// fn from4( arg1 : Arg1, arg2 : Arg2, arg3 : Arg3, arg4 : Arg4 ) -> Self; // } + // impl< T, E > From< ( E, ) > for T + // where + // T : From1< ( E, ) >, + // { + // /// Returns the argument unchanged. + // #[ inline( always ) ] + // fn from( src : T ) -> Self + // { + // Self::from1( src ) + // } + // } + + // not possible + // + // impl< T, F > From< T > for F + // where + // F : From1< T >, + // { + // /// Returns the argument unchanged. + // #[ inline( always ) ] + // fn from( src : T ) -> Self + // { + // Self::from1( src ) + // } + // } + /// /// Variadic constructor. /// - /// Implement traits [From_0], [From_1] up to MakeN to provide the interface to construct your structure with a different set of arguments. + /// Implement traits [`From1`] from tuple with fields and [std::convert::From] from tuple with fields to provide the interface to construct your structure with a different set of arguments. /// In this example structure, Struct1 could be constructed either without arguments, with a single argument, or with two arguments. /// - Constructor without arguments fills fields with zero. /// - Constructor with a single argument sets both fields to the value of the argument. /// - Constructor with 2 arguments set individual values of each field. /// /// ```rust - /// # #[ cfg( feature = "from" ) ] + /// # #[ cfg( all( feature = "derive_variadic_from", feature = "type_variadic_from" ) ) ] /// # { - /// use type_constructor::prelude::*; + /// use variadic_from::prelude::*; /// /// #[ derive( Debug, PartialEq ) ] /// struct Struct1 @@ -215,25 +250,25 @@ pub( crate ) mod private /// b : i32, /// } /// - /// impl From_0 for Struct1 + /// impl Default for Struct1 /// { - /// fn from_0() -> Self + /// fn default() -> Self /// { /// Self { a : 0, b : 0 } /// } /// } /// - /// impl From_1< i32 > for Struct1 + /// impl From1< i32 > for Struct1 /// { - /// fn from_1( val : i32 ) -> Self + /// fn from1( val : i32 ) -> Self /// { /// Self { a : val, b : val } /// } /// } /// - /// impl From_2< i32, i32 > for Struct1 + /// impl From2< i32, i32 > for Struct1 /// { - /// fn from_2( val1 : i32, val2 : i32 ) -> Self + /// fn from2( val1 : i32, val2 : i32 ) -> Self /// { /// Self { a : val1, b : val2 } /// } @@ -278,7 +313,7 @@ pub( crate ) mod private ) => { - $crate::wtools::From_0::from_0(); + ::core::default::Default::default(); }; ( @@ -286,7 +321,7 @@ pub( crate ) mod private ) => { - $crate::wtools::From_1::from_1( $Arg1 ); + $crate::From1::from1( $Arg1 ); }; ( @@ -294,7 +329,7 @@ pub( crate ) mod private ) => { - $crate::wtools::From_2::from_2( $Arg1, $Arg2 ); + $crate::From2::from2( $Arg1, $Arg2 ); }; ( @@ -302,7 +337,7 @@ pub( crate ) mod private ) => { - $crate::wtools::From_3::from_3( $Arg1, $Arg2, $Arg3 ); + $crate::From3::from3( $Arg1, $Arg2, $Arg3 ); }; // ( @@ -310,7 +345,7 @@ pub( crate ) mod private // ) // => // { - // $crate::wtools::From_4::from_4( $Arg1, $Arg2, $Arg3, $Arg4 ); + // $crate::From4::from4( $Arg1, $Arg2, $Arg3, $Arg4 ); // }; ( @@ -322,7 +357,7 @@ pub( crate ) mod private ( concat! ( - "Variadic constructor supports up to 1 arguments.\n", + "Variadic constructor supports up to 3 arguments.\n", "Open an issue if you need more.\n", "You passed:\n", stringify! @@ -382,11 +417,11 @@ pub mod prelude pub use super::private:: { - From_0, - From_1, + // From_0, + From1, Into1, - From_2, - From_3, + From2, + From3, from, diff --git a/module/core/variadic_from/src/wtools/mod.rs b/module/core/variadic_from/src/wtools/mod.rs deleted file mode 100644 index b53265f525..0000000000 --- a/module/core/variadic_from/src/wtools/mod.rs +++ /dev/null @@ -1,51 +0,0 @@ -//! -//! Types, which are extension of std. -//! - -/// Internal namespace. -pub( crate ) mod private -{ -} - -#[ cfg( feature = "type_variadic_from" ) ] -pub mod from; - -/// Protected namespace of the module. -pub mod protected -{ - #[ doc( inline ) ] - #[ allow( unused_imports ) ] - pub use super::orphan::*; -} - -#[ doc( inline ) ] -#[ allow( unused_imports ) ] -pub use protected::*; - -/// 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 -{ - #[ doc( inline ) ] - #[ allow( unused_imports ) ] - pub use super::prelude::*; - #[ cfg( feature = "type_variadic_from" ) ] - pub use super::from::orphan::*; -} - - -/// Prelude to use essentials: `use my_module::prelude::*`. -pub mod prelude -{ - #[ cfg( feature = "type_variadic_from" ) ] - #[ doc( inline ) ] - #[ allow( unused_imports ) ] - pub use super::from::prelude::*; -} diff --git a/module/core/variadic_from/tests/inc/auto_std_named_derive.rs b/module/core/variadic_from/tests/inc/auto_std_named_derive.rs new file mode 100644 index 0000000000..e194bc94b8 --- /dev/null +++ b/module/core/variadic_from/tests/inc/auto_std_named_derive.rs @@ -0,0 +1,17 @@ +#[ allow( unused_imports ) ] +use super::*; + +#[ allow( unused_imports ) ] +use the_module::exposed::*; + +#[ derive( Debug, PartialEq, Default, VariadicFrom ) ] +struct Struct1 +{ + a : i32, + b : i32, +} + +// Standard From and Into auto derive From1 and To_1. + +include!( "./only_test/from2_named.rs" ); +include!( "./only_test/from2_std_named.rs" ); diff --git a/module/core/variadic_from/tests/inc/auto_std_named_manual.rs b/module/core/variadic_from/tests/inc/auto_std_named_manual.rs new file mode 100644 index 0000000000..cade6e7496 --- /dev/null +++ b/module/core/variadic_from/tests/inc/auto_std_named_manual.rs @@ -0,0 +1,37 @@ +#[ allow( unused_imports ) ] +use super::*; + + +#[ allow( unused_imports ) ] +use the_module::exposed::*; + +#[ derive( Debug, PartialEq, Default ) ] +struct Struct1 +{ + a : i32, + b : i32, +} + +impl the_module::From1< i32 > for Struct1 +{ + fn from1( a : i32 ) -> Self { Self{ a : a, b : a } } +} + +impl the_module::From2< i32, i32 > for Struct1 +{ + fn from2( a : i32, b : i32 ) -> Self { Self{ a : a, b : b } } +} + +impl From< ( i32, i32 ) > for Struct1 +{ + #[ inline( always ) ] + fn from( ( a, b ) : ( i32, i32 ) ) -> Self + { + Self { a, b } + } +} + +// Standard From and Into auto derive From1 and To_1. + +include!( "./only_test/from2_named.rs" ); +include!( "./only_test/from2_std_named.rs" ); diff --git a/module/core/variadic_from/tests/inc/exports.rs b/module/core/variadic_from/tests/inc/exports.rs new file mode 100644 index 0000000000..cf498e0ac6 --- /dev/null +++ b/module/core/variadic_from/tests/inc/exports.rs @@ -0,0 +1,22 @@ +#[ allow( unused_imports ) ] +use super::*; + +// make sure all entities are exported + +mod m1 +{ + use super::*; + use the_module::variadic::{ From1, Into1, From2, From3, from }; +} + +mod m2 +{ + use super::*; + use the_module::prelude::{ From1, Into1, From2, From3, from }; +} + +mod m3 +{ + use super::*; + use the_module::exposed::{ From1, Into1, From2, From3, from }; +} diff --git a/module/core/variadic_from/tests/inc/from0_named_derive.rs b/module/core/variadic_from/tests/inc/from0_named_derive.rs new file mode 100644 index 0000000000..65009608d6 --- /dev/null +++ b/module/core/variadic_from/tests/inc/from0_named_derive.rs @@ -0,0 +1,13 @@ +#[ allow( unused_imports ) ] +use super::*; +use the_module::exposed::*; + +#[ derive( Debug, PartialEq, Default, VariadicFrom ) ] +struct Struct1; + +impl From< () > for Struct1 +{ + fn from( _a : () ) -> Self { Self::default() } +} + +include!( "./only_test/from0.rs" ); diff --git a/module/core/variadic_from/tests/inc/from0_named_manual.rs b/module/core/variadic_from/tests/inc/from0_named_manual.rs new file mode 100644 index 0000000000..11decd7b28 --- /dev/null +++ b/module/core/variadic_from/tests/inc/from0_named_manual.rs @@ -0,0 +1,14 @@ +#[ allow( unused_imports ) ] +use super::*; +use the_module::exposed::*; + +// #[ derive( Debug, PartialEq, Default, VariadicFrom ) ] +#[ derive( Debug, PartialEq, Default ) ] +struct Struct1; + +impl From< () > for Struct1 +{ + fn from( _a : () ) -> Self { Self::default() } +} + +include!( "./only_test/from0.rs" ); diff --git a/module/core/variadic_from/tests/inc/from0_unnamed_derive.rs b/module/core/variadic_from/tests/inc/from0_unnamed_derive.rs new file mode 100644 index 0000000000..0e6c6d7e74 --- /dev/null +++ b/module/core/variadic_from/tests/inc/from0_unnamed_derive.rs @@ -0,0 +1,13 @@ +#[ allow( unused_imports ) ] +use super::*; +use the_module::exposed::*; + +#[ derive( Debug, PartialEq, Default, VariadicFrom ) ] +struct Struct1(); + +impl From< () > for Struct1 +{ + fn from( _a : () ) -> Self { Self::default() } +} + +include!( "./only_test/from0.rs" ); diff --git a/module/core/variadic_from/tests/inc/from2_named_derive.rs b/module/core/variadic_from/tests/inc/from2_named_derive.rs new file mode 100644 index 0000000000..650d0a0189 --- /dev/null +++ b/module/core/variadic_from/tests/inc/from2_named_derive.rs @@ -0,0 +1,14 @@ +#[ allow( unused_imports ) ] +use super::*; + +use variadic_from::{ from, From1, From2, Into1 }; + + +#[ derive( Debug, PartialEq, variadic_from::VariadicFrom ) ] +struct Struct1 +{ + a : i32, + b : i32, +} + +include!( "./only_test/from2_named.rs" ); diff --git a/module/core/variadic_from/tests/inc/from2_named_manual.rs b/module/core/variadic_from/tests/inc/from2_named_manual.rs new file mode 100644 index 0000000000..fd206064e7 --- /dev/null +++ b/module/core/variadic_from/tests/inc/from2_named_manual.rs @@ -0,0 +1,27 @@ +#[ allow( unused_imports ) ] +use super::*; + +use variadic_from::{ from, From1, From2, Into1 }; + +#[ derive( Debug, PartialEq ) ] +struct Struct1 +{ + a : i32, + b : i32, +} + +impl variadic_from::From2< i32, i32 > for Struct1 +{ + fn from2( a : i32, b : i32 ) -> Self { Self{ a : a, b : b } } +} + +impl From< ( i32, i32 ) > for Struct1 +{ + #[ inline( always ) ] + fn from( ( a, b ) : ( i32, i32 ) ) -> Self + { + Self::from2( a, b ) + } +} + +include!( "./only_test/from2_named.rs" ); diff --git a/module/core/variadic_from/tests/inc/from2_unnamed_derive.rs b/module/core/variadic_from/tests/inc/from2_unnamed_derive.rs new file mode 100644 index 0000000000..159aaf4188 --- /dev/null +++ b/module/core/variadic_from/tests/inc/from2_unnamed_derive.rs @@ -0,0 +1,10 @@ +#[ allow( unused_imports ) ] +use super::*; + +use variadic_from::{ from, From1, From2, Into1 }; + + +#[ derive( Debug, PartialEq, variadic_from::VariadicFrom ) ] +struct Struct1( i32, i32 ); + +include!( "./only_test/from2_unnamed.rs" ); diff --git a/module/core/variadic_from/tests/inc/from2_unnamed_manual.rs b/module/core/variadic_from/tests/inc/from2_unnamed_manual.rs new file mode 100644 index 0000000000..6f4c678f8e --- /dev/null +++ b/module/core/variadic_from/tests/inc/from2_unnamed_manual.rs @@ -0,0 +1,23 @@ +#[ allow( unused_imports ) ] +use super::*; + +use variadic_from::{ from, From1, From2, Into1 }; + +#[ derive( Debug, PartialEq ) ] +struct Struct1( i32, i32 ); + +impl variadic_from::From2< i32, i32 > for Struct1 +{ + fn from2( a : i32, b : i32 ) -> Self { Self( a, b ) } +} + +impl From< ( i32, i32 ) > for Struct1 +{ + #[ inline( always ) ] + fn from( ( a, b ) : ( i32, i32 ) ) -> Self + { + Self::from2( a, b ) + } +} + +include!( "./only_test/from2_unnamed.rs" ); diff --git a/module/core/variadic_from/tests/inc/from4_beyond_named.rs b/module/core/variadic_from/tests/inc/from4_beyond_named.rs new file mode 100644 index 0000000000..76ddaa059b --- /dev/null +++ b/module/core/variadic_from/tests/inc/from4_beyond_named.rs @@ -0,0 +1,115 @@ +#[ allow( unused_imports ) ] +use super::*; + +/// IMPORTANT: length of struct should always be larget by one than +/// maximum number of supported arguments by `VariadicFrom`. +/// Currently it's 3, but if the length will be increased test should be extended too. +/// +/// `VariadicFrom` generates nothing in this case. +#[ test ] +fn from_named4() +{ + use the_module::{ Into1, VariadicFrom }; + + #[ derive( Default, Debug, PartialEq, VariadicFrom ) ] + // #[ debug ] + struct Struct1 + { + a : i32, + b : i32, + c : i32, + d : i32, + } + + impl the_module::From1< i32 > for Struct1 + { + fn from1( a : i32 ) -> Self { Self{ a, b : a, c : a, d : a } } + } + + impl the_module::From2< i32, i32 > for Struct1 + { + fn from2( a : i32, b : i32 ) -> Self { Self{ a, b, c : b, d : b } } + } + + impl the_module::From3< i32, i32, i32 > for Struct1 + { + fn from3( a : i32, b : i32, c : i32 ) -> Self { Self{ a, b, c, d : c } } + } + + // 0 + + let got : Struct1 = the_module::from!(); + let exp = Struct1{ a : 0, b : 0, c : 0, d : 0 }; + a_id!( got, exp ); + + // 1 + + let got : Struct1 = the_module::from!( 13 ); + let exp = Struct1{ a : 13, b : 13, c : 13, d : 13 }; + a_id!( got, exp ); + + let got : Struct1 = the_module::from!( ( 13, ) ); + let exp = Struct1{ a : 13, b : 13, c : 13, d : 13 }; + a_id!( got, exp ); + + let got : Struct1 = the_module::from!( ( ( 13, ), ) ); + let exp = Struct1{ a : 13, b : 13, c : 13, d : 13 }; + a_id!( got, exp ); + + let got : Struct1 = 13.to(); + let exp = Struct1{ a : 13, b : 13, c : 13, d : 13 }; + a_id!( got, exp ); + + let got : Struct1 = ( 13, ).to(); + let exp = Struct1{ a : 13, b : 13, c : 13, d : 13 }; + a_id!( got, exp ); + + let got : Struct1 = ( ( 13, ), ).to(); + let exp = Struct1{ a : 13, b : 13, c : 13, d : 13 }; + a_id!( got, exp ); + + // 2 + + let got : Struct1 = the_module::from!( 0, 1 ); + let exp = Struct1{ a : 0, b : 1, c : 1, d : 1 }; + a_id!( got, exp ); + + let got : Struct1 = the_module::from!( ( 0, 1 ) ); + let exp = Struct1{ a : 0, b : 1, c : 1, d : 1 }; + a_id!( got, exp ); + + let got : Struct1 = the_module::from!( ( ( 0, 1 ), ) ); + let exp = Struct1{ a : 0, b : 1, c : 1, d : 1 }; + a_id!( got, exp ); + + let got : Struct1 = ( 0, 1 ).to(); + let exp = Struct1{ a : 0, b : 1, c : 1, d : 1 }; + a_id!( got, exp ); + + let got : Struct1 = ( ( 0, 1 ), ).to(); + let exp = Struct1{ a : 0, b : 1, c : 1, d : 1 }; + a_id!( got, exp ); + + // 3 + + let got : Struct1 = the_module::from!( 0, 1, 2 ); + let exp = Struct1{ a : 0, b : 1, c : 2, d : 2 }; + a_id!( got, exp ); + + let got : Struct1 = the_module::from!( ( 0, 1, 2 ) ); + let exp = Struct1{ a : 0, b : 1, c : 2, d : 2 }; + a_id!( got, exp ); + + let got : Struct1 = the_module::from!( ( ( 0, 1, 2 ), ) ); + let exp = Struct1{ a : 0, b : 1, c : 2, d : 2 }; + a_id!( got, exp ); + + let got : Struct1 = ( 0, 1, 2 ).to(); + let exp = Struct1{ a : 0, b : 1, c : 2, d : 2 }; + a_id!( got, exp ); + + let got : Struct1 = ( ( 0, 1, 2 ), ).to(); + let exp = Struct1{ a : 0, b : 1, c : 2, d : 2 }; + a_id!( got, exp ); + +} diff --git a/module/core/variadic_from/tests/inc/from4_beyond_unnamed.rs b/module/core/variadic_from/tests/inc/from4_beyond_unnamed.rs new file mode 100644 index 0000000000..249a5f9e96 --- /dev/null +++ b/module/core/variadic_from/tests/inc/from4_beyond_unnamed.rs @@ -0,0 +1,115 @@ +#[ allow( unused_imports ) ] +use super::*; + +/// IMPORTANT: length of struct should always be larget by one than +/// maximum number of supported arguments by `VariadicFrom`. +/// Currently it's 3, but if the length will be increased test should be extended too. +/// +/// `VariadicFrom` generates nothing in this case. +#[ test ] +fn from_named4() +{ + use the_module::{ Into1, VariadicFrom }; + + #[ derive( Default, Debug, PartialEq, VariadicFrom ) ] + // #[ debug ] + struct Struct1 + ( + i32, + i32, + i32, + i32, + ); + + impl the_module::From1< i32 > for Struct1 + { + fn from1( a : i32 ) -> Self { Self( a, a, a, a ) } + } + + impl the_module::From2< i32, i32 > for Struct1 + { + fn from2( a : i32, b : i32 ) -> Self { Self( a, b, b, b ) } + } + + impl the_module::From3< i32, i32, i32 > for Struct1 + { + fn from3( a : i32, b : i32, c : i32 ) -> Self { Self( a, b, c, c ) } + } + + // 0 + + let got : Struct1 = the_module::from!(); + let exp = Struct1( 0, 0, 0, 0 ); + a_id!( got, exp ); + + // 1 + + let got : Struct1 = the_module::from!( 13 ); + let exp = Struct1( 13, 13, 13, 13 ); + a_id!( got, exp ); + + let got : Struct1 = the_module::from!( ( 13, ) ); + let exp = Struct1( 13, 13, 13, 13 ); + a_id!( got, exp ); + + let got : Struct1 = the_module::from!( ( ( 13, ), ) ); + let exp = Struct1( 13, 13, 13, 13 ); + a_id!( got, exp ); + + let got : Struct1 = 13.to(); + let exp = Struct1( 13, 13, 13, 13 ); + a_id!( got, exp ); + + let got : Struct1 = ( 13, ).to(); + let exp = Struct1( 13, 13, 13, 13 ); + a_id!( got, exp ); + + let got : Struct1 = ( ( 13, ), ).to(); + let exp = Struct1( 13, 13, 13, 13 ); + a_id!( got, exp ); + + // 2 + + let got : Struct1 = the_module::from!( 0, 1 ); + let exp = Struct1( 0, 1, 1, 1 ); + a_id!( got, exp ); + + let got : Struct1 = the_module::from!( ( 0, 1 ) ); + let exp = Struct1( 0, 1, 1, 1 ); + a_id!( got, exp ); + + let got : Struct1 = the_module::from!( ( ( 0, 1 ), ) ); + let exp = Struct1( 0, 1, 1, 1 ); + a_id!( got, exp ); + + let got : Struct1 = ( 0, 1 ).to(); + let exp = Struct1( 0, 1, 1, 1 ); + a_id!( got, exp ); + + let got : Struct1 = ( ( 0, 1 ), ).to(); + let exp = Struct1( 0, 1, 1, 1 ); + a_id!( got, exp ); + + // 3 + + let got : Struct1 = the_module::from!( 0, 1, 2 ); + let exp = Struct1( 0, 1, 2, 2 ); + a_id!( got, exp ); + + let got : Struct1 = the_module::from!( ( 0, 1, 2 ) ); + let exp = Struct1( 0, 1, 2, 2 ); + a_id!( got, exp ); + + let got : Struct1 = the_module::from!( ( ( 0, 1, 2 ), ) ); + let exp = Struct1( 0, 1, 2, 2 ); + a_id!( got, exp ); + + let got : Struct1 = ( 0, 1, 2 ).to(); + let exp = Struct1( 0, 1, 2, 2 ); + a_id!( got, exp ); + + let got : Struct1 = ( ( 0, 1, 2 ), ).to(); + let exp = Struct1( 0, 1, 2, 2 ); + a_id!( got, exp ); + +} diff --git a/module/core/variadic_from/tests/inc/from4_named_manual.rs b/module/core/variadic_from/tests/inc/from4_named_manual.rs new file mode 100644 index 0000000000..d1f5a62637 --- /dev/null +++ b/module/core/variadic_from/tests/inc/from4_named_manual.rs @@ -0,0 +1,43 @@ +#[ allow( unused_imports ) ] +use super::*; +use the_module::variadic::Into1; + +#[ derive( Debug, PartialEq ) ] +struct Struct1 +{ + a : i32, + b : i32, + c : i32, + d : i32, +} + +impl Default for Struct1 +{ + fn default() -> Self + { + let a = Default::default(); + let b = Default::default(); + let c = Default::default(); + let d = Default::default(); + Self{ a, b, c, d } + } +} + +impl the_module::From1< i32 > for Struct1 +{ + fn from1( a : i32 ) -> Self { Self{ a, b : a, c : a, d : a } } +} + +// impl the_module::From2< i32, i32 > for Struct1 +// { +// fn from2( a : i32, b : i32 ) -> Self { Self{ a, b, c : b, d : b } } +// } +// +// impl the_module::From3< i32, i32, i32 > for Struct1 +// { +// fn from3( a : i32, b : i32, c : i32 ) -> Self { Self{ a, b, c, d : c } } +// } + +include!( "./only_test/from4_named.rs" ); + +// diff --git a/module/core/variadic_from/tests/inc/from4_unnamed_manual.rs b/module/core/variadic_from/tests/inc/from4_unnamed_manual.rs new file mode 100644 index 0000000000..b6f50062ea --- /dev/null +++ b/module/core/variadic_from/tests/inc/from4_unnamed_manual.rs @@ -0,0 +1,37 @@ +#[ allow( unused_imports ) ] +use super::*; +use the_module::prelude::Into1; + +#[ derive( Debug, PartialEq ) ] +struct Struct1( i32, i32, i32, i32 ); + +impl Default for Struct1 +{ + fn default() -> Self + { + let a = Default::default(); + let b = Default::default(); + let c = Default::default(); + let d = Default::default(); + Self( a, b, c, d ) + } +} + +impl the_module::From1< i32 > for Struct1 +{ + fn from1( a : i32 ) -> Self { Self( a, a, a, a ) } +} + +// impl the_module::From2< i32, i32 > for Struct1 +// { +// fn from2( a : i32, b : i32 ) -> Self { Self( a, b, b, b ) } +// } +// +// impl the_module::From3< i32, i32, i32 > for Struct1 +// { +// fn from3( a : i32, b : i32, c : i32 ) -> Self { Self( a, b, c, c ) } +// } + +include!( "./only_test/from4_unnamed.rs" ); + +// diff --git a/module/core/variadic_from/tests/inc/mod.rs b/module/core/variadic_from/tests/inc/mod.rs index f9ab5a887a..ed70959fd2 100644 --- a/module/core/variadic_from/tests/inc/mod.rs +++ b/module/core/variadic_from/tests/inc/mod.rs @@ -1,14 +1,35 @@ +#![ allow( unused_imports ) ] + use super::*; #[ cfg( all( feature = "type_variadic_from" ) ) ] -mod variadic_from_manual_test; +mod from2_named_manual; +#[ cfg( all( feature = "derive_variadic_from", feature = "type_variadic_from" ) ) ] +mod from2_named_derive; #[ cfg( all( feature = "type_variadic_from" ) ) ] -mod variadic_from_manual_beyond_test; +mod from2_unnamed_manual; +#[ cfg( all( feature = "derive_variadic_from", feature = "type_variadic_from" ) ) ] +mod from2_unnamed_derive; -// #[ cfg( all( feature = "derive_variadic_from", feature = "type_variadic_from" ) ) ] -// mod variadic_from_derive_test; -// xxx : fix +#[ cfg( all( feature = "type_variadic_from" ) ) ] +mod from4_named_manual; +#[ cfg( all( feature = "type_variadic_from" ) ) ] +mod from4_unnamed_manual; +#[ cfg( all( feature = "type_variadic_from" ) ) ] +mod from4_beyond_named; +#[ cfg( all( feature = "type_variadic_from" ) ) ] +mod from4_beyond_unnamed; + +#[ cfg( all( feature = "type_variadic_from" ) ) ] +mod from0_named_manual; +#[ cfg( all( feature = "derive_variadic_from", feature = "type_variadic_from" ) ) ] +mod from0_named_derive; #[ cfg( all( feature = "derive_variadic_from", feature = "type_variadic_from" ) ) ] -mod variadic_from2_derive; +mod from0_unnamed_derive; + +#[ cfg( all( feature = "derive_variadic_from", feature = "type_variadic_from" ) ) ] +mod sample; +#[ cfg( all( feature = "type_variadic_from" ) ) ] +mod exports; diff --git a/module/core/variadic_from/tests/inc/only_test/from0.rs b/module/core/variadic_from/tests/inc/only_test/from0.rs new file mode 100644 index 0000000000..24c2d4ca76 --- /dev/null +++ b/module/core/variadic_from/tests/inc/only_test/from0.rs @@ -0,0 +1,50 @@ +#[ allow( unused_imports ) ] +use super::*; + +#[ test ] +fn from0() +{ + + // - from2 + + let got : Struct1 = from!(); + let exp = Struct1{}; + a_id!( got, exp ); + + let got : Struct1 = Struct1::default(); + let exp = Struct1{}; + a_id!( got, exp ); + + let got : Struct1 = Default::default(); + let exp = Struct1{}; + a_id!( got, exp ); + + // - from unit + + let got : Struct1 = from!( () ); + let exp = Struct1{}; + a_id!( got, exp ); + + let got : Struct1 = from!( ( (), ) ); + let exp = Struct1{}; + a_id!( got, exp ); + + let got : Struct1 = ().to(); + let exp = Struct1{}; + a_id!( got, exp ); + + let got : Struct1 = ( (), ).to(); + let exp = Struct1{}; + a_id!( got, exp ); + + // - std from unit + + let got : Struct1 = ().into(); + let exp = Struct1{}; + a_id!( got, exp ); + + let got : Struct1 = From::from( () ); + let exp = Struct1{}; + a_id!( got, exp ); + +} diff --git a/module/core/variadic_from/tests/inc/only_test/from2_named.rs b/module/core/variadic_from/tests/inc/only_test/from2_named.rs new file mode 100644 index 0000000000..451b501e94 --- /dev/null +++ b/module/core/variadic_from/tests/inc/only_test/from2_named.rs @@ -0,0 +1,53 @@ +#[ test ] +fn from2_named() +{ + + // - from2 + + let got : Struct1 = from!( 13, 14 ); + let exp = Struct1{ a : 13, b : 14 }; + a_id!( got, exp ); + + let got : Struct1 = Struct1::from2( 13, 14 ); + let exp = Struct1{ a : 13, b : 14 }; + a_id!( got, exp ); + + let got : Struct1 = from!( ( 13, 14 ) ); + let exp = Struct1{ a : 13, b : 14 }; + a_id!( got, exp ); + + // - from1 + + let got : Struct1 = Struct1::from1( ( 13, 14 ) ); + let exp = Struct1{ a : 13, b : 14 }; + a_id!( got, exp ); + + let got : Struct1 = from!( ( ( 13, 14 ), ) ); + let exp = Struct1{ a : 13, b : 14 }; + a_id!( got, exp ); + + let got : Struct1 = Struct1::from1( ( ( 13, 14 ), ) ); + let exp = Struct1{ a : 13, b : 14 }; + a_id!( got, exp ); + + // - to + + let got : Struct1 = ( 13, 14 ).to(); + let exp = Struct1{ a : 13, b : 14 }; + a_id!( got, exp ); + + let got : Struct1 = ( ( 13, 14 ), ).to(); + let exp = Struct1{ a : 13, b : 14 }; + a_id!( got, exp ); + + // - std + + let got : Struct1 = From::from( ( 13, 14 ) ); + let exp = Struct1{ a : 13, b : 14 }; + a_id!( got, exp ); + + let got : Struct1 = ( 13, 14 ).into(); + let exp = Struct1{ a : 13, b : 14 }; + a_id!( got, exp ); + +} diff --git a/module/core/variadic_from/tests/inc/only_test/from2_unnamed.rs b/module/core/variadic_from/tests/inc/only_test/from2_unnamed.rs new file mode 100644 index 0000000000..7063417045 --- /dev/null +++ b/module/core/variadic_from/tests/inc/only_test/from2_unnamed.rs @@ -0,0 +1,53 @@ +#[ test ] +fn from2_named() +{ + + // - from2 + + let got : Struct1 = from!( 13, 14 ); + let exp = Struct1( 13, 14 ); + a_id!( got, exp ); + + let got : Struct1 = Struct1::from2( 13, 14 ); + let exp = Struct1( 13, 14 ); + a_id!( got, exp ); + + let got : Struct1 = from!( ( 13, 14 ) ); + let exp = Struct1( 13, 14 ); + a_id!( got, exp ); + + // - from1 + + let got : Struct1 = Struct1::from1( ( 13, 14 ) ); + let exp = Struct1( 13, 14 ); + a_id!( got, exp ); + + let got : Struct1 = from!( ( ( 13, 14 ), ) ); + let exp = Struct1( 13, 14 ); + a_id!( got, exp ); + + let got : Struct1 = Struct1::from1( ( ( 13, 14 ), ) ); + let exp = Struct1( 13, 14 ); + a_id!( got, exp ); + + // - to + + let got : Struct1 = ( 13, 14 ).to(); + let exp = Struct1( 13, 14 ); + a_id!( got, exp ); + + let got : Struct1 = ( ( 13, 14 ), ).to(); + let exp = Struct1( 13, 14 ); + a_id!( got, exp ); + + // - std + + let got : Struct1 = From::from( ( 13, 14 ) ); + let exp = Struct1( 13, 14 ); + a_id!( got, exp ); + + let got : Struct1 = ( 13, 14 ).into(); + let exp = Struct1( 13, 14 ); + a_id!( got, exp ); + +} diff --git a/module/core/variadic_from/tests/inc/only_test/from4_named.rs b/module/core/variadic_from/tests/inc/only_test/from4_named.rs new file mode 100644 index 0000000000..70f84650ec --- /dev/null +++ b/module/core/variadic_from/tests/inc/only_test/from4_named.rs @@ -0,0 +1,47 @@ +#[ test ] +fn from4_named_fields() +{ + + let got : Struct1 = the_module::from!(); + let exp = Struct1{ a : 0, b : 0, c : 0, d : 0 }; + a_id!( got, exp ); + + let got : Struct1 = the_module::from!( 13 ); + let exp = Struct1{ a : 13, b : 13, c : 13, d : 13 }; + a_id!( got, exp ); + + // - from unit + + let got : Struct1 = the_module::from!( () ); + let exp = Struct1{ a : 0, b : 0, c : 0, d : 0 }; + a_id!( got, exp ); + + let got : Struct1 = the_module::from!( ( (), ) ); + let exp = Struct1{ a : 0, b : 0, c : 0, d : 0 }; + a_id!( got, exp ); + + let got : Struct1 = ().to(); + let exp = Struct1{ a : 0, b : 0, c : 0, d : 0 }; + a_id!( got, exp ); + + let got : Struct1 = ( (), ).to(); + let exp = Struct1{ a : 0, b : 0, c : 0, d : 0 }; + a_id!( got, exp ); + + // - negative + +// let got : Struct1 = the_module::from!( 0, 1 ); +// let exp = Struct1{ a : 0, b : 1, c : 1, d : 1 }; +// a_id!( got, exp ); +// +// let got : Struct1 = the_module::from!( 0, 1, 2 ); +// let exp = Struct1{ a : 0, b : 1, c : 2, d : 2 }; +// a_id!( got, exp ); +// +// let got : Struct1 = the_module::from!( 0, 1, 2, 3 ); +// let exp = Struct1{ a : 0, b : 1, c : 2, d : 3 }; +// a_id!( got, exp ); + + // qqq : write negative test + +} diff --git a/module/core/variadic_from/tests/inc/only_test/from4_unnamed.rs b/module/core/variadic_from/tests/inc/only_test/from4_unnamed.rs new file mode 100644 index 0000000000..ae9a26314e --- /dev/null +++ b/module/core/variadic_from/tests/inc/only_test/from4_unnamed.rs @@ -0,0 +1,50 @@ +#[ test ] +fn from4_tuple() +{ + + // #[ derive( Debug, PartialEq ) ] + // struct Struct1( i32, i32, i32, i32 ); + + let got : Struct1 = the_module::from!(); + let exp = Struct1( 0, 0, 0, 0 ); + a_id!( got, exp ); + + let got : Struct1 = the_module::from!( 13 ); + let exp = Struct1( 13, 13, 13, 13 ); + a_id!( got, exp ); + + // - from unit + + let got : Struct1 = the_module::from!( () ); + let exp = Struct1( 0, 0, 0, 0 ); + a_id!( got, exp ); + + let got : Struct1 = the_module::from!( ( (), ) ); + let exp = Struct1( 0, 0, 0, 0 ); + a_id!( got, exp ); + + let got : Struct1 = ().to(); + let exp = Struct1( 0, 0, 0, 0 ); + a_id!( got, exp ); + + let got : Struct1 = ( (), ).to(); + let exp = Struct1( 0, 0, 0, 0 ); + a_id!( got, exp ); + + // - negative + +// let got : Struct1 = the_module::from!( 0, 1 ); +// let exp = Struct1( 0, 1, 1, 1 ); +// a_id!( got, exp ); +// +// let got : Struct1 = the_module::from!( 0, 1, 2 ); +// let exp = Struct1( 0, 1, 2, 2 ); +// a_id!( got, exp ); +// +// let got : Struct1 = the_module::from!( 0, 1, 2, 3 ); +// let exp = Struct1( 0, 1, 2, 3 ); +// a_id!( got, exp ); + + // qqq : write negative test + +} diff --git a/module/core/variadic_from/tests/inc/only_test/variadic_from2_named.rs b/module/core/variadic_from/tests/inc/only_test/variadic_from2_named.rs deleted file mode 100644 index 926afb60a6..0000000000 --- a/module/core/variadic_from/tests/inc/only_test/variadic_from2_named.rs +++ /dev/null @@ -1,39 +0,0 @@ -{ - - // - - let got : StructNamedFields = from!( 13, 14 ); - let exp = StructNamedFields{ a : 13, b : 14 }; - a_id!( got, exp ); - - let got : StructNamedFields = StructNamedFields::from_2( 13, 14 ); - let exp = StructNamedFields{ a : 13, b : 14 }; - a_id!( got, exp ); - - let got : StructNamedFields = from!( ( 13, 14 ) ); - let exp = StructNamedFields{ a : 13, b : 14 }; - a_id!( got, exp ); - - let got : StructNamedFields = StructNamedFields::from_1( ( 13, 14 ) ); - let exp = StructNamedFields{ a : 13, b : 14 }; - a_id!( got, exp ); - - let got : StructNamedFields = from!( ( ( 13, 14 ), ) ); - let exp = StructNamedFields{ a : 13, b : 14 }; - a_id!( got, exp ); - - let got : StructNamedFields = StructNamedFields::from_1( ( ( 13, 14 ), ) ); - let exp = StructNamedFields{ a : 13, b : 14 }; - a_id!( got, exp ); - - let got : StructNamedFields = ( 13, 14 ).to(); - let exp = StructNamedFields{ a : 13, b : 14 }; - a_id!( got, exp ); - - let got : StructNamedFields = ( ( 13, 14 ), ).to(); - let exp = StructNamedFields{ a : 13, b : 14 }; - a_id!( got, exp ); - - // - -} \ No newline at end of file diff --git a/module/core/variadic_from/tests/inc/only_test/variadic_from_named.rs b/module/core/variadic_from/tests/inc/only_test/variadic_from_named.rs deleted file mode 100644 index 8772166857..0000000000 --- a/module/core/variadic_from/tests/inc/only_test/variadic_from_named.rs +++ /dev/null @@ -1,32 +0,0 @@ -{ - - // #[ derive( Debug, PartialEq ) ] - // struct StructNamedFields - // { - // a : i32, - // b : i32, - // c : i32, - // d : i32, - // } - - let got : StructNamedFields = the_module::from!(); - let exp = StructNamedFields{ a : 0, b : 0, c : 0, d : 0 }; - a_id!( got, exp ); - - let got : StructNamedFields = the_module::from!( 13 ); - let exp = StructNamedFields{ a : 13, b : 13, c : 13, d : 13 }; - a_id!( got, exp ); - -// let got : StructNamedFields = the_module::from!( 0, 1 ); -// let exp = StructNamedFields{ a : 0, b : 1, c : 1, d : 1 }; -// a_id!( got, exp ); -// -// let got : StructNamedFields = the_module::from!( 0, 1, 2 ); -// let exp = StructNamedFields{ a : 0, b : 1, c : 2, d : 2 }; -// a_id!( got, exp ); - - // let got : StructNamedFields = the_module::from!( 0, 1, 2, 3 ); - // let exp = StructNamedFields{ a : 0, b : 1, c : 2, d : 3 }; - // a_id!( got, exp ); - -} \ No newline at end of file diff --git a/module/core/variadic_from/tests/inc/only_test/variadic_from_tuple.rs b/module/core/variadic_from/tests/inc/only_test/variadic_from_tuple.rs deleted file mode 100644 index b984ceba3a..0000000000 --- a/module/core/variadic_from/tests/inc/only_test/variadic_from_tuple.rs +++ /dev/null @@ -1,27 +0,0 @@ -{ - - // #[ derive( Debug, PartialEq ) ] - // struct StructTuple( i32, i32, i32, i32 ); - - let got : StructTuple = the_module::from!(); - let exp = StructTuple( 0, 0, 0, 0 ); - a_id!( got, exp ); - - let got : StructTuple = the_module::from!( 13 ); - let exp = StructTuple( 13, 13, 13, 13 ); - a_id!( got, exp ); - -// let got : StructTuple = the_module::from!( 0, 1 ); -// let exp = StructTuple( 0, 1, 1, 1 ); -// a_id!( got, exp ); -// -// let got : StructTuple = the_module::from!( 0, 1, 2 ); -// let exp = StructTuple( 0, 1, 2, 2 ); -// a_id!( got, exp ); - - // qqq : write negative test - // let got : StructTuple = the_module::from!( 0, 1, 2, 3 ); - // let exp = StructTuple( 0, 1, 2, 3 ); - // a_id!( got, exp ); - -} diff --git a/module/core/variadic_from/tests/inc/sample.rs b/module/core/variadic_from/tests/inc/sample.rs new file mode 100644 index 0000000000..103aff658e --- /dev/null +++ b/module/core/variadic_from/tests/inc/sample.rs @@ -0,0 +1,49 @@ +#[ allow( unused_imports ) ] +use super::*; + +/// This test function validates the `VariadicFrom` trait implementation for the `MyStruct` struct. +/// It checks the conversion from tuples and individual values into an instance of `MyStruct`. +#[ test ] +fn sample() +{ + use variadic_from::exposed::*; + + // Define a struct `MyStruct` with fields `a` and `b`. + // The struct derives common traits like `Debug`, `PartialEq`, `Default`, and `VariadicFrom`. + #[ derive( Debug, PartialEq, Default, VariadicFrom ) ] + // Use `#[ debug ]` to expand and debug generate code. + // #[ debug ] + struct MyStruct + { + a : i32, + b : i32, + } + + // Implement the `From1` trait for `MyStruct`, which allows constructing a `MyStruct` instance + // from a single `i32` value by assigning it to both `a` and `b` fields. + impl From1< i32 > for MyStruct + { + fn from1( a : i32 ) -> Self { Self { a, b : a } } + } + + let got : MyStruct = from!(); + let exp = MyStruct { a : 0, b : 0 }; + assert_eq!( got, exp ); + + let got : MyStruct = from!( 13 ); + let exp = MyStruct { a : 13, b : 13 }; + assert_eq!( got, exp ); + + let got : MyStruct = from!( 13, 14 ); + let exp = MyStruct { a : 13, b : 14 }; + assert_eq!( got, exp ); + + let got : MyStruct = From::from( ( 13, 14 ) ); + let exp = MyStruct { a : 13, b : 14 }; + assert_eq!( got, exp ); + + let got : MyStruct = ( 13, 14 ).into(); + let exp = MyStruct { a : 13, b : 14 }; + assert_eq!( got, exp ); + +} diff --git a/module/core/variadic_from/tests/inc/variadic_from2_derive.rs b/module/core/variadic_from/tests/inc/variadic_from2_derive.rs deleted file mode 100644 index 88bd73f908..0000000000 --- a/module/core/variadic_from/tests/inc/variadic_from2_derive.rs +++ /dev/null @@ -1,110 +0,0 @@ -#[ allow( unused_imports ) ] -use super::*; - -/// Standard From and Into implemented for From_1. -#[ test ] -fn std_from_and_into_derive() -{ - #[ allow( unused_imports ) ] - use the_module::exposed::*; - - #[ derive( Debug, PartialEq, Default, VariadicFrom ) ] - struct StructNamedFields - { - a : i32, - b : i32, - } - - // - - include!( "./only_test/variadic_from2_named.rs" ); - - // - - let got : StructNamedFields = From::from( ( 13, 14 ) ); - let exp = StructNamedFields{ a : 13, b : 14 }; - a_id!( got, exp ); - - let got : StructNamedFields = ( 13, 14 ).into(); - let exp = StructNamedFields{ a : 13, b : 14 }; - a_id!( got, exp ); - -} - -/// Standard From and Into auto derive From_1 and To_1. -#[ test ] -fn auto_from_std_from_and_into() -{ - #[ allow( unused_imports ) ] - use the_module::exposed::*; - - #[ derive( Debug, PartialEq, Default ) ] - struct StructNamedFields - { - a : i32, - b : i32, - } - - // - - // impl the_module::wtools::From_2< i32, i32 > for StructNamedFields - // { - // fn from_2( a : i32, b : i32 ) -> Self { Self{ a, b } } - // } - - impl From< ( i32, i32 ) > for StructNamedFields - { - #[ inline( always ) ] - fn from( ( a, b ) : ( i32, i32 ) ) -> Self - { - Self { a, b } - } - } - - // - - let got : StructNamedFields = From::from( ( 13, 14 ) ); - let exp = StructNamedFields{ a : 13, b : 14 }; - a_id!( got, exp ); - - let got : StructNamedFields = ( 13, 14 ).into(); - let exp = StructNamedFields{ a : 13, b : 14 }; - a_id!( got, exp ); - - // - -// let got : StructNamedFields = from!( 13, 14 ); -// let exp = StructNamedFields{ a : 13, b : 14 }; -// a_id!( got, exp ); -// -// let got : StructNamedFields = StructNamedFields::from_2( 13, 14 ); -// let exp = StructNamedFields{ a : 13, b : 14 }; -// a_id!( got, exp ); -// -// let got : StructNamedFields = from!( ( 13, 14 ) ); -// let exp = StructNamedFields{ a : 13, b : 14 }; -// a_id!( got, exp ); -// -// let got : StructNamedFields = StructNamedFields::from_1( ( 13, 14 ) ); -// let exp = StructNamedFields{ a : 13, b : 14 }; -// a_id!( got, exp ); -// -// let got : StructNamedFields = from!( ( ( 13, 14 ), ) ); -// let exp = StructNamedFields{ a : 13, b : 14 }; -// a_id!( got, exp ); -// -// let got : StructNamedFields = StructNamedFields::from_1( ( ( 13, 14 ), ) ); -// let exp = StructNamedFields{ a : 13, b : 14 }; -// a_id!( got, exp ); -// -// let got : StructNamedFields = ( 13, 14 ).to(); -// let exp = StructNamedFields{ a : 13, b : 14 }; -// a_id!( got, exp ); -// -// let got : StructNamedFields = ( ( 13, 14 ), ).to(); -// let exp = StructNamedFields{ a : 13, b : 14 }; -// a_id!( got, exp ); - - // - -} diff --git a/module/core/variadic_from/tests/inc/variadic_from_derive_test.rs b/module/core/variadic_from/tests/inc/variadic_from_derive_test.rs deleted file mode 100644 index 622faa9e96..0000000000 --- a/module/core/variadic_from/tests/inc/variadic_from_derive_test.rs +++ /dev/null @@ -1,58 +0,0 @@ -#[ allow( unused_imports ) ] -use super::*; - -#[ test ] -fn from_named_fields() -{ - use the_module::prelude::*; - - #[ derive( Debug, PartialEq, the_module::VariadicFrom ) ] - struct StructNamedFields - { - a : i32, - b : i32, - c : i32, - d : i32, - } - - include!( "./only_test/variadic_from_named.rs" ); -} - -// - -#[ test ] -fn from_tuple() -{ - use the_module::prelude::*; - - #[ derive( Debug, PartialEq, the_module::VariadicFrom ) ] - struct StructTuple( i32, i32, i32, i32 ); - - include!( "./only_test/variadic_from_tuple.rs" ); -} - -// - -#[ test ] -fn sample() -{ - use the_module::exposed::*; - - #[ derive( Debug, PartialEq, the_module::VariadicFrom ) ] - struct MyStruct - { - a : i32, - b : i32, - } - - let got : MyStruct = from!(); - let exp = MyStruct { a : 0, b : 0 }; - a_id!( got, exp ); - - let got : MyStruct = from!( 13 ); - let exp = MyStruct { a : 13, b : 13 }; - a_id!( got, exp ); - -} - -// qqq : add to examples and to readme diff --git a/module/core/variadic_from/tests/inc/variadic_from_manual_beyond_test.rs b/module/core/variadic_from/tests/inc/variadic_from_manual_beyond_test.rs deleted file mode 100644 index 41b0b7e3e3..0000000000 --- a/module/core/variadic_from/tests/inc/variadic_from_manual_beyond_test.rs +++ /dev/null @@ -1,327 +0,0 @@ -#[ allow( unused_imports ) ] -use super::*; - -#[ test ] -fn from_named_fields() -{ - - #[ derive( Debug, PartialEq ) ] - struct StructNamedFields - { - a : i32, - b : i32, - c : i32, - d : i32, - } - - impl the_module::wtools::From_0 for StructNamedFields - { - fn from_0() -> Self - { - let a = Default::default(); - let b = Default::default(); - let c = Default::default(); - let d = Default::default(); - Self{ a, b, c, d } - } - } - - impl the_module::wtools::From_1< i32 > for StructNamedFields - { - fn from_1( a : i32 ) -> Self { Self{ a, b : a, c : a, d : a } } - } - - impl the_module::wtools::From_2< i32, i32 > for StructNamedFields - { - fn from_2( a : i32, b : i32 ) -> Self { Self{ a, b, c : b, d : b } } - } - - impl the_module::wtools::From_3< i32, i32, i32 > for StructNamedFields - { - fn from_3( a : i32, b : i32, c : i32 ) -> Self { Self{ a, b, c, d : c } } - } - - let got : StructNamedFields = the_module::from!(); - let exp = StructNamedFields{ a : 0, b : 0, c : 0, d : 0 }; - a_id!( got, exp ); - - let got : StructNamedFields = the_module::from!( 13 ); - let exp = StructNamedFields{ a : 13, b : 13, c : 13, d : 13 }; - a_id!( got, exp ); - - let got : StructNamedFields = the_module::from!( 0, 1 ); - let exp = StructNamedFields{ a : 0, b : 1, c : 1, d : 1 }; - a_id!( got, exp ); - - let got : StructNamedFields = the_module::from!( 0, 1, 2 ); - let exp = StructNamedFields{ a : 0, b : 1, c : 2, d : 2 }; - a_id!( got, exp ); - -} - -// - -#[ test ] -fn from_tuple() -{ - - #[ derive( Debug, PartialEq ) ] - struct StructTuple( i32, i32, i32, i32 ); - - impl the_module::wtools::From_0 for StructTuple - { - fn from_0() -> Self - { - let a = Default::default(); - let b = Default::default(); - let c = Default::default(); - let d = Default::default(); - Self( a, b, c, d ) - } - } - - impl the_module::wtools::From_1< i32 > for StructTuple - { - fn from_1( a : i32 ) -> Self { Self( a, a, a, a ) } - } - - impl the_module::wtools::From_2< i32, i32 > for StructTuple - { - fn from_2( a : i32, b : i32 ) -> Self { Self( a, b, b, b ) } - } - - impl the_module::wtools::From_3< i32, i32, i32 > for StructTuple - { - fn from_3( a : i32, b : i32, c : i32 ) -> Self { Self( a, b, c, c ) } - } - - let got : StructTuple = the_module::from!(); - let exp = StructTuple( 0, 0, 0, 0 ); - a_id!( got, exp ); - - let got : StructTuple = the_module::from!( 13 ); - let exp = StructTuple( 13, 13, 13, 13 ); - a_id!( got, exp ); - - let got : StructTuple = the_module::from!( 0, 1 ); - let exp = StructTuple( 0, 1, 1, 1 ); - a_id!( got, exp ); - - let got : StructTuple = the_module::from!( 0, 1, 2 ); - let exp = StructTuple( 0, 1, 2, 2 ); - a_id!( got, exp ); - -} - -// - -/// From_0 is auto implemented from Default. -#[ test ] -fn from0_from_default() -{ - - #[ derive( Debug, PartialEq, Default ) ] - struct StructNamedFields - { - a : i32, - b : i32, - } - - // impl the_module::wtools::From_0 for StructNamedFields - // { - // fn from_0() -> Self - // { - // let a = Default::default(); - // let b = Default::default(); - // Self{ a, b } - // } - // } - - let got : StructNamedFields = the_module::from!(); - let exp = StructNamedFields{ a : 0, b : 0 }; - a_id!( got, exp ); - - let got : StructNamedFields = the_module::From_0::from_0(); - let exp = StructNamedFields{ a : 0, b : 0 }; - a_id!( got, exp ); - - let got : StructNamedFields = Default::default(); - let exp = StructNamedFields{ a : 0, b : 0 }; - a_id!( got, exp ); - -} - -// - -/// Into1 is auto implemented from From_1. -/// From_1< ( All, ) > is auto implemented for From_1< All >. -#[ test ] -fn from_tuple_from_from1() -{ - use the_module::prelude::*; - - #[ derive( Debug, PartialEq, Default ) ] - struct StructNamedFields - { - a : i32, - b : i32, - c : i32, - d : i32, - } - - impl the_module::wtools::From_1< i32 > for StructNamedFields - { - fn from_1( a : i32 ) -> Self { Self{ a, b : a, c : a, d : a } } - } - - let got : StructNamedFields = from!( 13 ); - let exp = StructNamedFields{ a : 13, b : 13, c : 13, d : 13 }; - a_id!( got, exp ); - - let got : StructNamedFields = StructNamedFields::from_1( 13 ); - let exp = StructNamedFields{ a : 13, b : 13, c : 13, d : 13 }; - a_id!( got, exp ); - - let got : StructNamedFields = from!( ( 13, ) ); - let exp = StructNamedFields{ a : 13, b : 13, c : 13, d : 13 }; - a_id!( got, exp ); - - let got : StructNamedFields = StructNamedFields::from_1( ( 13, ) ); - let exp = StructNamedFields{ a : 13, b : 13, c : 13, d : 13 }; - a_id!( got, exp ); - - let got : StructNamedFields = from!( ( ( 13, ), ) ); - let exp = StructNamedFields{ a : 13, b : 13, c : 13, d : 13 }; - a_id!( got, exp ); - - let got : StructNamedFields = StructNamedFields::from_1( ( ( 13, ), ) ); - let exp = StructNamedFields{ a : 13, b : 13, c : 13, d : 13 }; - a_id!( got, exp ); - - let got : StructNamedFields = 13.to(); - let exp = StructNamedFields{ a : 13, b : 13, c : 13, d : 13 }; - a_id!( got, exp ); - - let got : StructNamedFields = ( 13, ).to(); - let exp = StructNamedFields{ a : 13, b : 13, c : 13, d : 13 }; - a_id!( got, exp ); - - let got : StructNamedFields = ( ( 13, ), ).to(); - let exp = StructNamedFields{ a : 13, b : 13, c : 13, d : 13 }; - a_id!( got, exp ); - -} - -// - -/// Into1 is auto implemented from From_1. -/// From_1< ( All1, All2 ) > is auto implemented for From_2< All1, All2 >. -#[ test ] -fn from_tuple_from_from2() -{ - use the_module::prelude::*; - - #[ derive( Debug, PartialEq, Default ) ] - struct StructNamedFields - { - a : i32, - b : i32, - c : i32, - d : i32, - } - - impl the_module::wtools::From_2< i32, i32 > for StructNamedFields - { - fn from_2( a : i32, b : i32 ) -> Self { Self{ a, b, c : b, d : b } } - } - - let got : StructNamedFields = from!( 13, 14 ); - let exp = StructNamedFields{ a : 13, b : 14, c : 14, d : 14 }; - a_id!( got, exp ); - - let got : StructNamedFields = StructNamedFields::from_2( 13, 14 ); - let exp = StructNamedFields{ a : 13, b : 14, c : 14, d : 14 }; - a_id!( got, exp ); - - let got : StructNamedFields = from!( ( 13, 14 ) ); - let exp = StructNamedFields{ a : 13, b : 14, c : 14, d : 14 }; - a_id!( got, exp ); - - let got : StructNamedFields = StructNamedFields::from_1( ( 13, 14 ) ); - let exp = StructNamedFields{ a : 13, b : 14, c : 14, d : 14 }; - a_id!( got, exp ); - - let got : StructNamedFields = from!( ( ( 13, 14 ), ) ); - let exp = StructNamedFields{ a : 13, b : 14, c : 14, d : 14 }; - a_id!( got, exp ); - - let got : StructNamedFields = StructNamedFields::from_1( ( ( 13, 14 ), ) ); - let exp = StructNamedFields{ a : 13, b : 14, c : 14, d : 14 }; - a_id!( got, exp ); - - let got : StructNamedFields = ( 13, 14 ).to(); - let exp = StructNamedFields{ a : 13, b : 14, c : 14, d : 14 }; - a_id!( got, exp ); - - let got : StructNamedFields = ( ( 13, 14 ), ).to(); - let exp = StructNamedFields{ a : 13, b : 14, c : 14, d : 14 }; - a_id!( got, exp ); - -} - -// - -/// Into1 is auto implemented from From_1. -/// From_1< ( All1, All2, All3 ) > is auto implemented for From_3< All1, All2, All3 >. -#[ test ] -fn from_tuple_from_from3() -{ - use the_module::prelude::*; - - #[ derive( Debug, PartialEq, Default ) ] - struct StructNamedFields - { - a : i32, - b : i32, - c : i32, - d : i32, - } - - impl the_module::wtools::From_3< i32, i32, i32 > for StructNamedFields - { - fn from_3( a : i32, b : i32, c : i32 ) -> Self { Self{ a, b, c, d : c } } - } - - let got : StructNamedFields = from!( 13, 14, 15 ); - let exp = StructNamedFields{ a : 13, b : 14, c : 15, d : 15 }; - a_id!( got, exp ); - - let got : StructNamedFields = StructNamedFields::from_3( 13, 14, 15 ); - let exp = StructNamedFields{ a : 13, b : 14, c : 15, d : 15 }; - a_id!( got, exp ); - - let got : StructNamedFields = from!( ( 13, 14, 15 ) ); - let exp = StructNamedFields{ a : 13, b : 14, c : 15, d : 15 }; - a_id!( got, exp ); - - let got : StructNamedFields = StructNamedFields::from_1( ( 13, 14, 15 ) ); - let exp = StructNamedFields{ a : 13, b : 14, c : 15, d : 15 }; - a_id!( got, exp ); - - let got : StructNamedFields = from!( ( ( 13, 14, 15 ), ) ); - let exp = StructNamedFields{ a : 13, b : 14, c : 15, d : 15 }; - a_id!( got, exp ); - - let got : StructNamedFields = StructNamedFields::from_1( ( ( 13, 14, 15 ), ) ); - let exp = StructNamedFields{ a : 13, b : 14, c : 15, d : 15 }; - a_id!( got, exp ); - - let got : StructNamedFields = ( 13, 14, 15 ).to(); - let exp = StructNamedFields{ a : 13, b : 14, c : 15, d : 15 }; - a_id!( got, exp ); - - let got : StructNamedFields = ( ( 13, 14, 15 ), ).to(); - let exp = StructNamedFields{ a : 13, b : 14, c : 15, d : 15 }; - a_id!( got, exp ); - -} diff --git a/module/core/variadic_from/tests/inc/variadic_from_manual_test.rs b/module/core/variadic_from/tests/inc/variadic_from_manual_test.rs deleted file mode 100644 index 4ad3a048ac..0000000000 --- a/module/core/variadic_from/tests/inc/variadic_from_manual_test.rs +++ /dev/null @@ -1,86 +0,0 @@ -#[ allow( unused_imports ) ] -use super::*; - -#[ test ] -fn from_named_fields() -{ - - #[ derive( Debug, PartialEq ) ] - struct StructNamedFields - { - a : i32, - b : i32, - c : i32, - d : i32, - } - - impl the_module::wtools::From_0 for StructNamedFields - { - fn from_0() -> Self - { - let a = Default::default(); - let b = Default::default(); - let c = Default::default(); - let d = Default::default(); - Self{ a, b, c, d } - } - } - - impl the_module::wtools::From_1< i32 > for StructNamedFields - { - fn from_1( a : i32 ) -> Self { Self{ a, b : a, c : a, d : a } } - } - -// impl the_module::wtools::From_2< i32, i32 > for StructNamedFields -// { -// fn from_2( a : i32, b : i32 ) -> Self { Self{ a, b, c : b, d : b } } -// } -// -// impl the_module::wtools::From_3< i32, i32, i32 > for StructNamedFields -// { -// fn from_3( a : i32, b : i32, c : i32 ) -> Self { Self{ a, b, c, d : c } } -// } - - include!( "./only_test/variadic_from_named.rs" ); -} - -// - -#[ test ] -fn from_tuple() -{ - - #[ derive( Debug, PartialEq ) ] - struct StructTuple( i32, i32, i32, i32 ); - - impl the_module::wtools::From_0 for StructTuple - { - fn from_0() -> Self - { - let a = Default::default(); - let b = Default::default(); - let c = Default::default(); - let d = Default::default(); - Self( a, b, c, d ) - } - } - - impl the_module::wtools::From_1< i32 > for StructTuple - { - fn from_1( a : i32 ) -> Self { Self( a, a, a, a ) } - } - -// impl the_module::wtools::From_2< i32, i32 > for StructTuple -// { -// fn from_2( a : i32, b : i32 ) -> Self { Self( a, b, b, b ) } -// } -// -// impl the_module::wtools::From_3< i32, i32, i32 > for StructTuple -// { -// fn from_3( a : i32, b : i32, c : i32 ) -> Self { Self( a, b, c, c ) } -// } - - include!( "./only_test/variadic_from_tuple.rs" ); -} - -// diff --git a/module/core/variadic_from/tests/variadic_from_tests.rs b/module/core/variadic_from/tests/variadic_from_tests.rs index 2b5d216700..463bba061f 100644 --- a/module/core/variadic_from/tests/variadic_from_tests.rs +++ b/module/core/variadic_from/tests/variadic_from_tests.rs @@ -1,9 +1,10 @@ #[ allow( unused_imports ) ] use variadic_from as the_module; +#[ allow( unused_imports ) ] +use variadic_from; +#[ allow( unused_imports ) ] use test_tools::exposed::*; -// #[ path = "inc.rs" ] #[ cfg( feature = "enabled" ) ] mod inc; - diff --git a/module/core/wtools/Cargo.toml b/module/core/wtools/Cargo.toml index 78c0329a51..3d2ec71314 100644 --- a/module/core/wtools/Cargo.toml +++ b/module/core/wtools/Cargo.toml @@ -308,7 +308,7 @@ derive_from_str = [ "derive", "derive_tools/derive_from_str", "parse-display" ] derive_clone_dyn = [ "derive", "derive_tools/derive_clone_dyn" ] # derive_clone_dyn_use_std = [ "derive_clone_dyn", "derive_tools/derive_clone_dyn_use_std" ] -derive_clone_dyn_use_alloc = [ "derive_clone_dyn", "derive_tools/derive_clone_dyn_use_alloc" ] +# derive_clone_dyn_use_alloc = [ "derive_clone_dyn", "derive_tools/derive_clone_dyn_use_alloc" ] # dt @@ -373,7 +373,7 @@ diagnostics_compiletime_assertions = [ "diagnostics_tools/diagnostics_compiletim nightly = [] # must be empty no_std = [] -use_alloc = [ "no_std" ] +use_alloc = [ "no_std", "derive_tools/use_alloc" ] enabled = [] # xxx : qqq : should it be filled by all non_std? diff --git a/module/core/wtools/Readme.md b/module/core/wtools/Readme.md index 5bd27d0838..742902bad7 100644 --- a/module/core/wtools/Readme.md +++ b/module/core/wtools/Readme.md @@ -7,125 +7,6 @@ Collection of general purpose tools for solving problems. Fundamentally extend the language without spoiling, so may be used solely or in conjunction with another module of such kind. -### Basic Use Case :: implements - - - - - - -```rust ignore,editable -#[ cfg( feature = "typing_default" ) ] -{ - use wtools::prelude::*; - println!( "implements!( 13_i32 => Copy ) : {}", implements!( 13_i32 => Copy ) ); - println!( "implements!( Box::new( 13_i32 ) => Copy ) : {}", implements!( Box::new( 13_i32 ) => Copy ) ); -} -``` - -### Basic Use Case :: type constructors - -In Rust, you often need to wrap a given type into a new one. -The role of the orphan rules in particular is basically to prevent you from implementing external traits for external types. -To overcome the restriction developer usually wrap the external type into a tuple introducing a new type. -Type constructor does exactly that and auto-implement traits From, Into, Deref and few more for the constructed type. - -Macro [types](https://docs.rs/type_constructor/latest/type_constructor/types/macro.types.html) is responsible for generating code for Single, Pair, Homopair, Many. Each type constructor has its own keyword for that, but Pair and Homopair use the same keyword difference in a number of constituent types. It is possible to define all types at once. - - - - - -```rust ignore,editable -#[ cfg( feature = "dt_default" ) ] -{ - use wtools::prelude::*; - - types! - { - - single MySingle : f32; - single SingleWithParametrized : std::sync::Arc< T : Copy >; - single SingleWithParameter : < T >; - - pair MyPair : f32; - pair PairWithParametrized : std::sync::Arc< T1 : Copy >, std::sync::Arc< T2 : Copy >; - pair PairWithParameter : < T1, T2 >; - - pair MyHomoPair : f32; - pair HomoPairWithParametrized : std::sync::Arc< T : Copy >; - pair HomoPairWithParameter : < T >; - - many MyMany : f32; - many ManyWithParametrized : std::sync::Arc< T : Copy >; - many ManyWithParameter : < T >; - - } -} -``` - -### Basic Use Case :: make - variadic constructor - -Implement traits [From_0], [From_1] up to MakeN to provide the interface to construct your structure with a different set of arguments. -In this example structure, Struct1 could be constructed either without arguments, with a single argument, or with two arguments. -- Constructor without arguments fills fields with zero. -- Constructor with a single argument sets both fields to the value of the argument. -- Constructor with 2 arguments set individual values of each field. - - - - - -```rust ignore -#[ cfg( feature = "dt_default" ) ] -{ - use wtools::prelude::*; - - #[ derive( Debug, PartialEq ) ] - struct Struct1 - { - a : i32, - b : i32, - } - - impl From_0 for Struct1 - { - fn from_0() -> Self - { - Self { a : 0, b : 0 } - } - } - - impl From_1< i32 > for Struct1 - { - fn from_1( val : i32 ) -> Self - { - Self { a : val, b : val } - } - } - - impl From_2< i32, i32 > for Struct1 - { - fn from_2( val1 : i32, val2 : i32 ) -> Self - { - Self { a : val1, b : val2 } - } - } - - let got : Struct1 = from!(); - let exp = Struct1{ a : 0, b : 0 }; - assert_eq!( got, exp ); - - let got : Struct1 = from!( 13 ); - let exp = Struct1{ a : 13, b : 13 }; - assert_eq!( got, exp ); - - let got : Struct1 = from!( 1, 3 ); - let exp = Struct1{ a : 1, b : 3 }; - assert_eq!( got, exp ); -} -``` - ### To add to your project ```sh diff --git a/module/move/crates_tools/Cargo.toml b/module/move/crates_tools/Cargo.toml index d6b77678b2..4120a358df 100644 --- a/module/move/crates_tools/Cargo.toml +++ b/module/move/crates_tools/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "crates_tools" -version = "0.8.0" +version = "0.10.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", diff --git a/module/move/crates_tools/Readme.md b/module/move/crates_tools/Readme.md index 6c6a1e3740..dabc50fb6c 100644 --- a/module/move/crates_tools/Readme.md +++ b/module/move/crates_tools/Readme.md @@ -2,7 +2,7 @@ # Module :: crates_tools - [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/module_crates_tools_push.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/module_crates_tools_push.yml) [![docs.rs](https://img.shields.io/docsrs/crates_tools?color=e3e8f0&logo=docs.rs)](https://docs.rs/crates_tools) [![Open in Gitpod](https://raster.shields.io/static/v1?label=try&message=online&color=eee&logo=gitpod&logoColor=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module/move/crates_tools/examples/crates_tools_trivial.rs,RUN_POSTFIX=--example%20/home/sakapoi/Документи/wTools_fork/module/move/crates_tools/examples/crates_tools_trivial/https://github.com/Wandalen/wTools) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) + [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/module_crates_tools_push.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/module_crates_tools_push.yml) [![docs.rs](https://img.shields.io/docsrs/crates_tools?color=e3e8f0&logo=docs.rs)](https://docs.rs/crates_tools) [![Open in Gitpod](https://raster.shields.io/static/v1?label=try&message=online&color=eee&logo=gitpod&logoColor=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fmove%2Fcrates_tools%2Fexamples%2Fcrates_tools_trivial.rs,RUN_POSTFIX=--example%20crates_tools_trivial/https://github.com/Wandalen/wTools) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) Tools to analyse crate files. diff --git a/module/move/crates_tools/src/lib.rs b/module/move/crates_tools/src/lib.rs index 569549e54f..20a89cd7cf 100644 --- a/module/move/crates_tools/src/lib.rs +++ b/module/move/crates_tools/src/lib.rs @@ -1,13 +1,8 @@ #![ 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/crates_tools/latest/crates_tools/" ) ] - #![ doc = include_str!( concat!( env!( "CARGO_MANIFEST_DIR" ), "/", "Readme.md" ) ) ] -//! -//! Tools to analyse crate files. -//! - /// Internal namespace. #[ cfg( feature = "enabled" ) ] pub( crate ) mod private diff --git a/module/move/optimization_tools/src/hybrid_optimizer/sim_anneal.rs b/module/move/optimization_tools/src/hybrid_optimizer/sim_anneal.rs index c176729441..f7d4c5743a 100644 --- a/module/move/optimization_tools/src/hybrid_optimizer/sim_anneal.rs +++ b/module/move/optimization_tools/src/hybrid_optimizer/sim_anneal.rs @@ -1,8 +1,8 @@ //! Implementation of Simulated Annealing for Hybrid Optimizer. -use derive_tools::{ FromInner, InnerFrom, exposed::Display }; +use derive_tools::{ From, InnerFrom, exposed::Display }; /// Represents temperature of SA process. -#[ derive( Default, Debug, Display, Clone, Copy, PartialEq, PartialOrd, FromInner, InnerFrom ) ] +#[ derive( Default, Debug, Display, Clone, Copy, PartialEq, PartialOrd, From, InnerFrom ) ] pub struct Temperature( f64 ); impl Temperature @@ -27,7 +27,7 @@ impl From< f32 > for Temperature // use derive_tools::{ Add, Sub, Mul, Div, AddAssign, SubAssign, MulAssign, DivAssign }; /// Struct that represents coefficient to change temperature value. -#[ derive( Debug, Display, Clone, Copy, PartialEq, PartialOrd, FromInner, InnerFrom ) ] +#[ derive( Debug, Display, Clone, Copy, PartialEq, PartialOrd, From, InnerFrom ) ] // #[ derive( Add, Sub, Mul, Div, AddAssign, SubAssign, MulAssign, DivAssign ) ] pub struct TemperatureFactor( pub f64 ); @@ -83,12 +83,12 @@ pub struct LinearTempSchedule impl TemperatureSchedule for LinearTempSchedule { - fn calculate_next_temp( &self, prev_temp : Temperature ) -> Temperature + fn calculate_next_temp( &self, prev_temp : Temperature ) -> Temperature { Temperature::from( prev_temp.unwrap() * self.coefficient.unwrap() + self.constant.unwrap() ) } - fn reset_temperature( &self, prev_temp : Temperature ) -> Temperature + fn reset_temperature( &self, prev_temp : Temperature ) -> Temperature { Temperature( prev_temp.unwrap() + self.reset_increase_value.unwrap() ) } diff --git a/module/move/optimization_tools/src/problems/sudoku/sudoku.rs b/module/move/optimization_tools/src/problems/sudoku/sudoku.rs index b016fa4cda..eac6a5dbda 100644 --- a/module/move/optimization_tools/src/problems/sudoku/sudoku.rs +++ b/module/move/optimization_tools/src/problems/sudoku/sudoku.rs @@ -4,7 +4,7 @@ use std::collections::HashSet; use crate::hybrid_optimizer::*; use crate::problems::sudoku::*; -use derive_tools::{ FromInner, InnerFrom, exposed::Display }; +use derive_tools::{ From, InnerFrom, exposed::Display }; use deterministic_rand::{ Hrng, Rng, seq::SliceRandom }; use iter_tools::Itertools; @@ -72,7 +72,7 @@ pub fn cells_pair_random_in_block( initial : &Board, block : BlockIndex, hrng : } /// Represents number of errors in sudoku board. -#[ derive( Default, Debug, Display, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Hash, FromInner, InnerFrom ) ] +#[ derive( Default, Debug, Display, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Hash, From, InnerFrom ) ] pub struct SudokuCost( usize ); // xxx : derive, please @@ -113,13 +113,13 @@ impl Individual for SudokuPerson { true } - else + else { false } } - fn fitness( &self ) -> usize + fn fitness( &self ) -> usize { self.cost.into() } @@ -153,7 +153,7 @@ impl SudokuPerson { let old_cross_error = self.board.cross_error( mutagen.cell1 ) + self.board.cross_error( mutagen.cell2 ); - + log::trace!( "cells_swap( {:?}, {:?} )", mutagen.cell1, mutagen.cell2 ); self.board.cells_swap( mutagen.cell1, mutagen.cell2 ); self.cost = SudokuCost( self.cost.unwrap() - old_cross_error ) ; @@ -174,8 +174,8 @@ impl SudokuPerson pub fn mutagen( &self, initial : &Board, hrng : Hrng ) -> SudokuMutagen { let mutagen; - loop - { + loop + { let rng_ref = hrng.rng_ref(); let mut rng = rng_ref.lock().unwrap(); let block : BlockIndex = rng.gen(); @@ -191,7 +191,7 @@ impl SudokuPerson } /// Represents single change(mutation) which contains indeces of two swapped cells. It is used to generate new state of the board for sudoku solving process. -#[ derive( PartialEq, Eq, Clone, Debug, FromInner, InnerFrom ) ] +#[ derive( PartialEq, Eq, Clone, Debug, From, InnerFrom ) ] pub struct SudokuMutagen { /// Index of cell swapped in mutation. @@ -221,12 +221,12 @@ impl InitialProblem for SudokuInitial { type Person = SudokuPerson; - fn get_random_person( &self, hrng : Hrng ) -> SudokuPerson + fn get_random_person( &self, hrng : Hrng ) -> SudokuPerson { SudokuPerson::new( &self.board, hrng.clone() ) } - fn evaluate( &self, person : &SudokuPerson ) -> f64 + fn evaluate( &self, person : &SudokuPerson ) -> f64 { person.board.total_error() as f64 } @@ -241,11 +241,11 @@ impl MutationOperator for RandomPairInBlockMutation type Person = SudokuPerson; type Problem = SudokuInitial; - fn mutate( &self, hrng : Hrng, person : &mut Self::Person, context : &Self::Problem ) + fn mutate( &self, hrng : Hrng, person : &mut Self::Person, context : &Self::Problem ) { let mutagen : SudokuMutagen = - loop - { + loop + { let rng_ref = hrng.rng_ref(); let mut rng = rng_ref.lock().unwrap(); let block : BlockIndex = rng.gen(); @@ -257,7 +257,7 @@ impl MutationOperator for RandomPairInBlockMutation }.into(); let old_cross_error = person.board.cross_error( mutagen.cell1 ) + person.board.cross_error( mutagen.cell2 ); - + log::trace!( "cells_swap( {:?}, {:?} )", mutagen.cell1, mutagen.cell2 ); person.board.cells_swap( mutagen.cell1, mutagen.cell2 ); person.cost = SudokuCost( person.cost.unwrap() - old_cross_error ); @@ -301,7 +301,7 @@ impl CrossoverOperator for MultiplePointsBlockCrossover child_storage[ usize::from( cell_index ) ] = parent_block[ index ]; } } - else + else { let parent_block = parent2.board.block( i ).collect_vec(); let cells = parent2.board.block_cells( i ); @@ -311,7 +311,7 @@ impl CrossoverOperator for MultiplePointsBlockCrossover } } } - + let child = SudokuPerson::with_board( Board::new( child_storage ) ); child } @@ -324,7 +324,7 @@ pub struct BestRowsColumnsCrossover; impl CrossoverOperator for BestRowsColumnsCrossover { type Person = < SudokuInitial as InitialProblem >::Person; - + fn crossover( &self, _hrng : Hrng, parent1 : &Self::Person, parent2 : &Self::Person ) -> Self::Person { let mut rows_costs = vec![ Vec::new(); 2 ]; @@ -393,7 +393,7 @@ impl CrossoverOperator for BestRowsColumnsCrossover child2_storage[ usize::from( cell_index ) ] = parent_block[ index ]; } } - else + else { let parent_block = parent2.board.block( BlockIndex::from( ( j as u8, i as u8 ) ) ).collect_vec(); let cells = parent2.board.block_cells( BlockIndex::from( ( j as u8, i as u8 ) ) ); @@ -411,6 +411,6 @@ impl CrossoverOperator for BestRowsColumnsCrossover .unwrap() ; - SudokuPerson::with_board( min_board ) + SudokuPerson::with_board( min_board ) } } diff --git a/module/move/optimization_tools/src/problems/traveling_salesman.rs b/module/move/optimization_tools/src/problems/traveling_salesman.rs index 2c3e5bb9a1..2eab112158 100644 --- a/module/move/optimization_tools/src/problems/traveling_salesman.rs +++ b/module/move/optimization_tools/src/problems/traveling_salesman.rs @@ -17,7 +17,7 @@ use std::collections::HashMap; use crate::hybrid_optimizer::*; -use derive_tools::{ FromInner, InnerFrom }; +use derive_tools::{ From, InnerFrom }; use deterministic_rand::{ Hrng, seq::{ SliceRandom, IteratorRandom } }; use iter_tools::Itertools; @@ -89,7 +89,7 @@ pub struct Node< T > pub struct NodeIndex( pub usize ); /// Weight of graph edge. -#[ derive( Debug, FromInner, InnerFrom, Clone, Copy ) ] +#[ derive( Debug, From, InnerFrom, Clone, Copy ) ] pub struct EdgeWeight( pub f64 ); /// Edge for undirected weighted graph. diff --git a/module/move/wca/Cargo.toml b/module/move/wca/Cargo.toml index edabb9d0d6..f6f277f90b 100644 --- a/module/move/wca/Cargo.toml +++ b/module/move/wca/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wca" -version = "0.15.0" +version = "0.17.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", @@ -50,6 +50,7 @@ former = { workspace = true, features = [ "default" ] } log = "0.4" #closure = "0.3" textdistance = { version = "1.0", optional = true } # fuzzy commands search +indexmap = "2.2.6" [dev-dependencies] test_tools = { workspace = true } diff --git a/module/move/wca/Readme.md b/module/move/wca/Readme.md index a9b9c0b4e4..b808fce2bc 100644 --- a/module/move/wca/Readme.md +++ b/module/move/wca/Readme.md @@ -2,7 +2,7 @@ # Module :: wca - [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/module_wca_push.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/module_wca_push.yml) [![docs.rs](https://img.shields.io/docsrs/wca?color=e3e8f0&logo=docs.rs)](https://docs.rs/wca) [![Open in Gitpod](https://raster.shields.io/static/v1?label=try&message=online&color=eee&logo=gitpod&logoColor=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module/move/wca/examples/wca_trivial.rs,RUN_POSTFIX=--example%20/home/sakapoi/Документи/wTools_fork/module/move/wca/examples/wca_trivial/https://github.com/Wandalen/wTools) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) + [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/module_wca_push.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/module_wca_push.yml) [![docs.rs](https://img.shields.io/docsrs/wca?color=e3e8f0&logo=docs.rs)](https://docs.rs/wca) [![Open in Gitpod](https://raster.shields.io/static/v1?label=try&message=online&color=eee&logo=gitpod&logoColor=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fmove%2Fwca%2Fexamples%2Fwca_trivial.rs,RUN_POSTFIX=--example%20wca_trivial/https://github.com/Wandalen/wTools) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) The tool to make CLI ( commands user interface ). It is able to aggregate external binary applications, as well as functions, which are written in your language. diff --git a/module/move/wca/examples/wca_trivial.rs b/module/move/wca/examples/wca_trivial.rs index 272923ecf5..c228e6e20a 100644 --- a/module/move/wca/examples/wca_trivial.rs +++ b/module/move/wca/examples/wca_trivial.rs @@ -2,7 +2,7 @@ //! A trivial example. //! -use wca::{ CommandsAggregator, Type, VerifiedCommand }; +use wca::{ CommandsAggregator, Order, Type, VerifiedCommand }; fn f1( o : VerifiedCommand ) { @@ -19,16 +19,17 @@ fn exit() fn main() { let ca = CommandsAggregator::former() + .command( "exit" ) + .hint( "just exit" ) + .routine( || exit() ) + .end() .command( "echo" ) .hint( "prints all subjects and properties" ) .subject().hint( "Subject" ).kind( Type::String ).optional( true ).end() .property( "property" ).hint( "simple property" ).kind( Type::String ).optional( true ).end() .routine( f1 ) .end() - .command( "exit" ) - .hint( "just exit" ) - .routine( || exit() ) - .end() + .order( Order::Lexicography ) .perform() ; diff --git a/module/move/wca/src/ca/aggregator.rs b/module/move/wca/src/ca/aggregator.rs index 1174de448f..1d81a4e945 100644 --- a/module/move/wca/src/ca/aggregator.rs +++ b/module/move/wca/src/ca/aggregator.rs @@ -27,6 +27,17 @@ pub( crate ) mod private }; use wtools::Itertools; + /// Order of commands and properties. + #[ derive( Debug, Default, Clone, Copy, Eq, PartialOrd, PartialEq ) ] + pub enum Order + { + /// Natures order. + #[ default ] + Nature, + /// Lexicography order. + Lexicography, + } + /// Validation errors that can occur in application. #[ derive( Error, Debug ) ] pub enum ValidationError @@ -100,8 +111,8 @@ pub( crate ) mod private /// ``` #[ derive( Debug ) ] #[ derive( former::Former ) ] - #[ storage_fields( help_generator : HelpGeneratorFn, help_variants : HashSet< HelpVariants > ) ] - #[ mutator( custom = true ) ] + #[ storage_fields( help_generator : HelpGeneratorFn, help_variants : HashSet< HelpVariants >, order : Order ) ] + #[ mutator( custom ) ] // #[ debug ] pub struct CommandsAggregator { @@ -111,7 +122,7 @@ pub( crate ) mod private #[ former( default = Parser ) ] parser : Parser, - #[ scalar( setter = false, hint = false ) ] + #[ scalar( setter = false ) ] #[ former( default = Executor::former().form() ) ] executor : Executor, @@ -127,19 +138,20 @@ pub( crate ) mod private { let ca = storage; let dictionary = ca.dictionary.get_or_insert_with( Dictionary::default ); + dictionary.order = ca.order.unwrap_or_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 ); + HelpVariants::All.generate( &help_generator, dictionary, ca.order.unwrap_or_default() ); } else { for help in help_variants.iter().sorted() { - help.generate( &help_generator, dictionary ); + help.generate( &help_generator, dictionary, ca.order.unwrap_or_default() ); } } } @@ -158,6 +170,7 @@ pub( crate ) mod private where IntoName : Into< String >, { + let name = name.into(); let on_end = | command : CommandFormerStorage, super_former : Option< Self > | -> Self { let mut super_former = super_former.unwrap(); @@ -279,4 +292,5 @@ crate::mod_interface! exposed use CommandsAggregatorFormer; exposed use Error; exposed use ValidationError; + exposed use Order; } diff --git a/module/move/wca/src/ca/executor/routine.rs b/module/move/wca/src/ca/executor/routine.rs index 146a290639..9165c4486a 100644 --- a/module/move/wca/src/ca/executor/routine.rs +++ b/module/move/wca/src/ca/executor/routine.rs @@ -126,7 +126,7 @@ pub( crate ) mod private } } - // qqq : make 0-arguments, 1-argument, 2-arguments, 3 arguments versions + // aaa : make 0-arguments, 1-argument, 2-arguments, 3 arguments versions // aaa : done. now it works with the following variants: // fn(), fn(args), fn(props), fn(args, props), fn(context), fn(context, args), fn(context, props), fn(context, args, props) @@ -297,7 +297,7 @@ pub( crate ) mod private } } - // qqq : why Rc is necessary? why not just box? + // aaa : why Rc is necessary? why not just box? // aaa : to be able to clone Routines impl PartialEq for Routine diff --git a/module/move/wca/src/ca/facade.rs b/module/move/wca/src/ca/facade.rs index a5a466cba6..cdc9edb599 100644 --- a/module/move/wca/src/ca/facade.rs +++ b/module/move/wca/src/ca/facade.rs @@ -65,7 +65,7 @@ pub( crate ) mod private /// The name of the property. pub name : &'a str, /// The hint for the property. - pub hint : &'a str, + pub debug : &'a str, /// The tag representing the property's type. pub tag : Type, } diff --git a/module/move/wca/src/ca/formatter.rs b/module/move/wca/src/ca/formatter.rs index d21979acdf..368ec8e88f 100644 --- a/module/move/wca/src/ca/formatter.rs +++ b/module/move/wca/src/ca/formatter.rs @@ -3,6 +3,7 @@ pub( crate ) mod private use crate::*; use wtools::Itertools; + use ca::aggregator::private::Order; /// - #[ derive( Debug, Clone, PartialEq ) ] @@ -12,18 +13,18 @@ pub( crate ) mod private Another, } - pub fn md_generator( grammar : &Dictionary ) -> String + pub fn md_generator( grammar : &Dictionary, order: Order ) -> String { - let text = grammar.commands - .iter() - .sorted_by_key( |( name, _ )| *name ) + let text = grammar.commands() + .into_iter() .map( |( name, cmd )| { let subjects = cmd.subjects.iter().fold( String::new(), | _, _ | format!( " `[argument]`" ) ); let properties = if cmd.properties.is_empty() { " " } else { " `[properties]` " }; format! ( - "[.{name}{subjects}{properties}](#{}{}{})", + "[.{}{subjects}{properties}](#{}{}{})", + name, name.replace( '.', "" ), if cmd.subjects.is_empty() { "" } else { "-argument" }, if cmd.properties.is_empty() { "" } else { "-properties" }, @@ -36,16 +37,15 @@ pub( crate ) mod private let list_of_commands = format!( "## Commands\n\n{}", text ); - let about_each_command = grammar.commands - .iter() - .sorted_by_key( |( name, _ )| *name ) + let about_each_command = grammar.commands() + .into_iter() .map( |( name, cmd )| { let subjects = cmd.subjects.iter().fold( String::new(), | _, _ | format!( " `[Subject]`" ) ); let properties = if cmd.properties.is_empty() { " " } else { " `[properties]` " }; let hint = if cmd.hint.is_empty() { &cmd.long_hint } else { &cmd.hint }; - let heading = format!( "## .{name}{subjects}{properties}\n__{}__\n", hint ); + let heading = format!( "## .{}{subjects}{properties}\n__{}__\n", name, hint ); let hint = if cmd.long_hint.is_empty() { &cmd.hint } else { &cmd.long_hint }; let full_subjects = cmd @@ -59,13 +59,12 @@ pub( crate ) mod private ) .join( "\n" ); let full_properties = cmd - .properties - .iter() - .sorted_by_key( |( name, _ )| *name ) + .properties( order ) + .into_iter() .map ( |( name, value )| - format!( "\n- {}{name} - {} `[{:?}]`", if value.optional { "`< optional >` " } else { "" }, value.hint, value.kind ) + format!( "\n- {}{} - {} `[{:?}]`", if value.optional { "`< optional >` " } else { "" }, value.hint, name, value.kind ) ) .join( "\n" ); // aaa : for Bohdan : toooooo log lines. 130 is max diff --git a/module/move/wca/src/ca/grammar/command.rs b/module/move/wca/src/ca/grammar/command.rs index 2f12e03921..38a02583a4 100644 --- a/module/move/wca/src/ca/grammar/command.rs +++ b/module/move/wca/src/ca/grammar/command.rs @@ -2,10 +2,10 @@ pub( crate ) mod private { use crate::*; - use { Handler, Routine, Type }; - - use std::collections::HashMap; + use std::collections::{ HashMap }; + use indexmap::IndexMap; use former::{ Former, StoragePreform }; + use wtools::Itertools; /// A description of a Value in a command. Used to specify the expected type and provide a hint for the Value. /// @@ -36,6 +36,7 @@ pub( crate ) mod private { name : String, // qqq : how to re-use ValueDescriptionFormer without additional end? + // #[subform_scalar] // value : ValueDescription, /// providing guidance to the user for entering a valid value hint : String, @@ -86,7 +87,6 @@ pub( crate ) mod private #[ derive( Debug, Clone, PartialEq, Eq ) ] #[ derive( Former ) ] - // #[ debug ] pub struct Command { /// Command common hint. @@ -96,14 +96,14 @@ pub( crate ) mod private /// Phrase descriptor for command. pub phrase : String, /// Command subjects hints and types. - #[ subform( setter = true ) ] + #[ subform_entry( setter = true ) ] pub subjects : Vec< ValueDescription >, /// Hints and types for command options. - pub properties : HashMap< String, ValueDescription >, + pub properties : IndexMap< String, ValueDescription >, /// Map of aliases. // Aliased key -> Original key pub properties_aliases : HashMap< String, String >, - // qqq : for Bohdan : routine should also be here + // aaa : for Bohdan : routine should also be here // aaa : here it is // qqq : make it usable and remove default(?) /// The type `Routine` represents the specific implementation of the routine. @@ -112,6 +112,24 @@ pub( crate ) mod private pub routine : Routine, } + impl Command + { + pub( crate ) fn properties( &self, order : Order ) -> Vec< ( &String, &ValueDescription ) > + { + match order + { + Order::Nature => + { + self.properties.iter().map( | ( key, value ) | ( key, value ) ).collect() + } + Order::Lexicography => + { + self.properties.iter().map( | ( key, value ) | ( key, value ) ).sorted_by_key( | ( k, _ ) | *k ).collect() + } + } + } + } + impl< Definition > CommandFormer< Definition > where Definition : former::FormerDefinition< Storage = < Command as former::EntityToStorage >::Storage >, @@ -179,7 +197,7 @@ pub( crate ) mod private /// It returns a `ValueDescriptionFormer` which can be used to further build the super-former. pub fn subject( self ) -> ValueDescriptionAsSubformer< Self, impl ValueDescriptionAsSubformerEnd< Self > > { - self._subjects_add() + self._subjects_subform_entry() } /// Sets the name and other properties of the current property. @@ -200,6 +218,7 @@ pub( crate ) mod private 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 a9a79d198a..06c8479f8b 100644 --- a/module/move/wca/src/ca/grammar/dictionary.rs +++ b/module/move/wca/src/ca/grammar/dictionary.rs @@ -1,10 +1,9 @@ pub( crate ) mod private { use crate::*; - - use { Command }; - use std::collections::HashMap; use former::Former; + use indexmap::IndexMap; + use wtools::Itertools; // qqq : `Former` does not handle this situation well @@ -16,12 +15,14 @@ pub( crate ) mod private /// A collection of commands. /// - /// This structure holds a hashmap of commands where each command is mapped to its name. + /// This structure holds a btreemap of commands where each command is mapped to its name. #[ derive( Debug, Default, Former, Clone ) ] pub struct Dictionary { - #[ scalar( setter = false, hint = false ) ] - pub( crate ) commands : HashMap< String, Command >, + #[ scalar( setter = false ) ] + pub( crate ) commands : IndexMap< String, Command >, + #[ scalar( setter = false ) ] + pub( crate ) order : Order, } // qqq : IDK how to integrate it into the `CommandsAggregatorFormer` @@ -31,9 +32,8 @@ pub( crate ) mod private pub fn command( mut self, command : Command ) -> Self { let mut commands = self.storage.commands.unwrap_or_default(); - commands.extend([( command.phrase.clone(), command )]); + commands.insert( command.phrase.clone(), command ); self.storage.commands = Some( commands ); - self } } @@ -67,7 +67,7 @@ pub( crate ) mod private { self.commands.get( name ) } - + /// Find commands that match a given name part. /// /// This function accepts a `name_part` parameter which is of generic type `NamePart`. @@ -86,6 +86,22 @@ pub( crate ) mod private { self.commands.values().filter( | command | command.phrase.starts_with( name_part.as_ref() ) ).collect() } + + /// asd + pub fn commands( &self ) -> Vec< ( &String, &Command ) > + { + match self.order + { + Order::Nature => + { + self.commands.iter().map( | ( key, value ) | ( key, value ) ).collect() + } + Order::Lexicography => + { + self.commands.iter().map( | ( key, value ) | ( key, value ) ).sorted_by_key( | ( key, _ ) | *key ).collect() + } + } + } } } diff --git a/module/move/wca/src/ca/grammar/mod.rs b/module/move/wca/src/ca/grammar/mod.rs index f3539a6694..f31e992b38 100644 --- a/module/move/wca/src/ca/grammar/mod.rs +++ b/module/move/wca/src/ca/grammar/mod.rs @@ -1,8 +1,9 @@ crate::mod_interface! { /// User grammar settings. + /// By using this module, you can define a new commands and provide a detailed descriptions of them. layer command; - /// - + /// A dictionary is a collection of commands that can be easily looked up and used. layer dictionary; /// Available types for arguments. layer types; diff --git a/module/move/wca/src/ca/help.rs b/module/move/wca/src/ca/help.rs index 028a79347e..cc8c897b70 100644 --- a/module/move/wca/src/ca/help.rs +++ b/module/move/wca/src/ca/help.rs @@ -4,14 +4,20 @@ pub( crate ) mod private use ca:: { Command, - Routine, Type, formatter::private::{ HelpFormat, md_generator }, + Routine, + Type, + formatter::private:: + { + HelpFormat, + md_generator + }, + tool::table::format_table, }; use wtools::Itertools; use std::rc::Rc; use error_tools::for_app::anyhow; use former::Former; - use ca::tool::table::format_table; // qqq : for Bohdan : it should transparent mechanist which patch list of commands, not a stand-alone mechanism @@ -53,6 +59,8 @@ pub( crate ) mod private pub description_detailing : LevelOfDetail, /// If enabled - shows complete description of subjects and properties pub with_footer : bool, + /// Order of property and commands. + pub order : Order, } // qqq : for Barsik : make possible to change properties order @@ -90,13 +98,14 @@ pub( crate ) mod private LevelOfDetail::None => "".into(), _ if command.subjects.is_empty() => "".into(), LevelOfDetail::Simple => "< properties >".into(), - LevelOfDetail::Detailed => command.properties.iter().map( |( n, v )| format!( "< {n}:{}{:?} >", if v.optional { "?" } else { "" }, v.kind ) ).collect::< Vec< _ > >().join( " " ), + LevelOfDetail::Detailed => command.properties( dictionary.order ).iter().map( |( n, v )| format!( "< {}:{}{:?} >", if v.optional { "?" } else { "" }, n, v.kind ) ).collect::< Vec< _ > >().join( " " ), }; let footer = if o.with_footer { let full_subjects = command.subjects.iter().map( | subj | format!( "- {} [{}{:?}]", subj.hint, if subj.optional { "?" } else { "" }, subj.kind ) ).join( "\n\t" ); - let full_properties = format_table( command.properties.iter().sorted_by_key( |( name, _ )| *name ).map( |( name, value )| [ name.clone(), format!( "- {} [{}{:?}]", value.hint, if value.optional { "?" } else { "" }, value.kind ) ] ) ).unwrap().replace( '\n', "\n\t" ); + let full_properties = format_table( command.properties( dictionary.order ).into_iter().map( | ( name, value ) | [ name.clone(), format!( "- {} [{}{:?}]", value.hint, if value.optional { "?" } else { "" }, value.kind ) ] ) ).unwrap().replace( '\n', "\n\t" ); + format! ( "{}{}", @@ -130,13 +139,11 @@ pub( crate ) mod private } else { - let rows = dictionary.commands - .iter() - .sorted_by_key( |( name, _ )| *name ) + let rows = dictionary.commands() + .into_iter() .map( |( _, cmd )| cmd ) .map( for_single_command ) .map( | row | [ row.name, row.args, row.hint ] ); - format_table( rows ).unwrap() } } @@ -158,17 +165,17 @@ pub( crate ) mod private impl HelpVariants { /// Generates help commands - pub fn generate( &self, helper : &HelpGeneratorFn, dictionary : &mut Dictionary ) + pub fn generate( &self, helper : &HelpGeneratorFn, dictionary : &mut Dictionary, order : Order ) { match self { HelpVariants::All => { - self.general_help( helper, dictionary ); + self.general_help( helper, dictionary, order ); self.subject_command_help( helper, dictionary ); // self.dot_command_help( helper, dictionary ); }, - HelpVariants::General => self.general_help( helper, dictionary ), + HelpVariants::General => self.general_help( helper, dictionary, order ), HelpVariants::SubjectCommand => self.subject_command_help( helper, dictionary ), _ => unimplemented!() // HelpVariants::DotCommand => self.dot_command_help( helper, dictionary ), @@ -176,7 +183,7 @@ pub( crate ) mod private } // .help - fn general_help( &self, helper : &HelpGeneratorFn, dictionary : &mut Dictionary ) + fn general_help( &self, helper : &HelpGeneratorFn, dictionary : &mut Dictionary, order : Order ) { let phrase = "help".to_string(); @@ -201,22 +208,22 @@ pub( crate ) mod private }; if format == HelpFormat::Markdown { - println!( "Help command\n{text}", text = md_generator( &grammar ) ); + println!( "Help command\n{text}", text = md_generator( &grammar, order ) ); } else { + let options = HelpGeneratorOptions::former() + .command_prefix( "." ) + .description_detailing( LevelOfDetail::Simple ) + .subject_detailing( LevelOfDetail::Simple ) + .property_detailing( LevelOfDetail::Simple ); println! ( "Help command\n\n{text}", text = generator.exec ( &grammar, - HelpGeneratorOptions::former() - .command_prefix( "." ) - .description_detailing( LevelOfDetail::Simple ) - .subject_detailing( LevelOfDetail::Simple ) - .property_detailing( LevelOfDetail::Simple ) - .form() + options.form() ) ); } @@ -266,9 +273,9 @@ pub( crate ) mod private .description_detailing( LevelOfDetail::Detailed ) .subject_detailing( LevelOfDetail::Simple ) .property_detailing( LevelOfDetail::Simple ) - .with_footer( true ) - .form(); - let text = generator.exec( &grammar, args ); + .with_footer( true ); + + let text = generator.exec( &grammar, args.form() ); println!( "Help command\n\n{text}" ); } diff --git a/module/move/wca/src/ca/mod.rs b/module/move/wca/src/ca/mod.rs index de9ccf9392..9333e5ac5b 100644 --- a/module/move/wca/src/ca/mod.rs +++ b/module/move/wca/src/ca/mod.rs @@ -11,11 +11,9 @@ crate::mod_interface! /// This component is responsible for performing layer executor; - // /// This component is responsible for aggregating all commands - // layer commands_aggregator; - - /// User input + /// Provides functionality for working with input data, including asking user questions and converting various string representations into a uniform `Input` struct. layer input; + // /// The missing batteries of WCA. // layer facade; /// Genera-purpose tools which might be moved out one day. @@ -23,10 +21,11 @@ crate::mod_interface! /// Responsible for aggregating all commands that the user defines, and for parsing and executing them layer aggregator; - /// Helper commands + /// This module provides functionality for generating help content for commands. layer help; /// Responsible for generating Markdown formatted documentation for commands layer formatter; - // qqq : for Bohdan : write concise documentations + // aaa : for Bohdan : write concise documentations + // aaa : Is this enough or is more needed? } diff --git a/module/move/wca/src/ca/parser/command.rs b/module/move/wca/src/ca/parser/command.rs index 1d39e9bf34..116142c709 100644 --- a/module/move/wca/src/ca/parser/command.rs +++ b/module/move/wca/src/ca/parser/command.rs @@ -7,9 +7,10 @@ pub( crate ) mod private /// A `Program` consists of one or more commannd /// /// The program can be executed by iterating over each commands and executing it - // qqq : xxx : for Bohdan : Commands should be here instead of Namespace - // qqq : remove concept Namespace - // qqq : introduce concept Dictionary for grammar + // aaa : xxx : for Bohdan : Commands should be here instead of Namespace + // aaa : remove concept Namespace + // aaa : introduce concept Dictionary for grammar + // aaa : done #[ derive( Debug, Clone, PartialEq, Eq ) ] pub struct Program< Command > { diff --git a/module/move/wca/src/ca/parser/mod.rs b/module/move/wca/src/ca/parser/mod.rs index 47a8d4c325..6d21385d36 100644 --- a/module/move/wca/src/ca/parser/mod.rs +++ b/module/move/wca/src/ca/parser/mod.rs @@ -1,8 +1,13 @@ crate::mod_interface! { - /// Parsed command + /// This module defines a raw representation of parsed commands, providing a foundation for further processing and + /// transformation into other formats. The raw representation captures the essential information about each command in + /// a straightforward and easy-to-work-with format, allowing for efficient manipulation and subsequent conversion to + /// other representations. layer command; - /// Parser. + /// This module is responsible for processing command-line arguments and parsing them into a raw representation of a + /// program containing multiple parsed commands. The input list of arguments is transformed into a structured format, + /// allowing the program to efficiently handle and manipulate the parsed commands. layer parser; } diff --git a/module/move/wca/src/ca/tool/mod.rs b/module/move/wca/src/ca/tool/mod.rs index a5700ae916..637dcae457 100644 --- a/module/move/wca/src/ca/tool/mod.rs +++ b/module/move/wca/src/ca/tool/mod.rs @@ -1,7 +1,7 @@ crate::mod_interface! { - /// Format table + /// It takes a table of data and format it into a human-readable string layer table; } diff --git a/module/move/wca/src/ca/verifier/mod.rs b/module/move/wca/src/ca/verifier/mod.rs index 8053a7d259..7ed35ae7b9 100644 --- a/module/move/wca/src/ca/verifier/mod.rs +++ b/module/move/wca/src/ca/verifier/mod.rs @@ -2,8 +2,6 @@ crate::mod_interface! { /// Represents a grammatically correct command with a phrase descriptor, a list of command subjects, and a set of command options.. layer command; - /// Converts from raw to executable. + /// Provides a set of tools for processing and validating input, extracting relevant information, and converting raw data into a standardized format. layer verifier; - // /// Available types for arguments. - // layer types; } diff --git a/module/move/wca/src/ca/verifier/verifier.rs b/module/move/wca/src/ca/verifier/verifier.rs index 3c51962e47..b443000f7d 100644 --- a/module/move/wca/src/ca/verifier/verifier.rs +++ b/module/move/wca/src/ca/verifier/verifier.rs @@ -5,10 +5,10 @@ pub( crate ) mod private use ca::grammar::command::ValueDescription; // use former::Former; use std::collections::HashMap; + use indexmap::IndexMap; use wtools::{ error, error::Result, err }; use ca::help::private::{ HelpGeneratorOptions, LevelOfDetail, generate_help_content }; - // TODO: Remove Clone /// Converts a `ParsedCommand` to a `VerifiedCommand` by performing validation and type casting on values. /// /// ``` @@ -33,47 +33,7 @@ pub( crate ) mod private /// # } /// ``` #[ derive( Debug, Clone ) ] - // #[ derive( Former ) ] pub struct Verifier; - // qqq : delete on completion - // { - // // TODO: Make getters - // /// all available commands - // #[ setter( false ) ] - // pub commands : &'a Dictionary, // qqq : for Bohdan : <- introduce Dictionary for HashMap< String, Vec< Command > > - // } - - // impl VerifierFormer - // { - // /// Insert a command to the commands list - // pub fn command( mut self, command : Command ) -> Self - // { - // let mut commands = self.storage.commands.unwrap_or_default(); - // - // let command_variants = commands.entry( command.phrase.to_owned() ).or_insert_with( Vec::new ); - // command_variants.push( command ); - // - // self.storage.commands = Some( commands ); - // self - // } - // - // /// Expands the list of commands with received commands - // pub fn commands< V >( mut self, commands : V ) -> Self - // where - // V : Into< Vec< Command > > - // { - // let mut self_commands = self.storage.commands.unwrap_or_default(); - // - // for command in commands.into() - // { - // let command_variants = self_commands.entry( command.phrase.to_owned() ).or_insert_with( Vec::new ); - // command_variants.push( command ); - // } - // - // self.storage.commands = Some( self_commands ); - // self - // } - // } impl Verifier { @@ -104,7 +64,7 @@ pub( crate ) mod private let sim = dictionary .commands .iter() - .map( |( name, c )| ( jaro.for_str( name, user_input ).nsim(), c ) ) + .map( |( name, c )| ( jaro.for_str( name.as_str(), user_input ).nsim(), c ) ) .max_by( |( s1, _ ), ( s2, _ )| s1.total_cmp( s2 ) ); if let Some(( sim, variant )) = sim { @@ -120,7 +80,7 @@ pub( crate ) mod private fn get_count_from_properties ( - properties : &HashMap< String, ValueDescription >, + properties : &IndexMap< String, ValueDescription >, properties_aliases : &HashMap< String, String >, raw_properties : &HashMap< String, String > ) -> usize diff --git a/module/move/wca/tests/assets/wca_hello_test/src/main.rs b/module/move/wca/tests/assets/wca_hello_test/src/main.rs deleted file mode 100644 index 796622c3e5..0000000000 --- a/module/move/wca/tests/assets/wca_hello_test/src/main.rs +++ /dev/null @@ -1,16 +0,0 @@ -fn main() -{ - use wca::{ Type, VerifiedCommand }; - - let ca = wca::CommandsAggregator::former() - .command( "echo" ) - .hint( "prints all subjects and properties" ) - .subject().hint( "Subject" ).kind( Type::String ).optional( true ).end() - .property( "property" ).hint( "simple property" ).kind( Type::String ).optional( true ).end() - .routine( | o : VerifiedCommand | { println!( "= Args\n{:?}\n\n= Properties\n{:?}\n", o.args, o.props ) } ) - .end() - .perform(); - - let args = std::env::args().skip( 1 ).collect::< Vec< String > >(); - ca.perform( args ).unwrap(); -} \ No newline at end of file diff --git a/module/move/wca/tests/inc/commands_aggregator/help.rs b/module/move/wca/tests/inc/commands_aggregator/help.rs index 3c4f01f6a6..1df2be062e 100644 --- a/module/move/wca/tests/inc/commands_aggregator/help.rs +++ b/module/move/wca/tests/inc/commands_aggregator/help.rs @@ -1,23 +1,8 @@ -use std::fs::File; +use std::fs::{DirBuilder, File}; use std::io::Write; use std::path::Path; use std::process::{Command, Stdio}; -use assert_fs::fixture::PathCopy; -const ASSET_PATH : &str = concat!( env!("CARGO_MANIFEST_DIR"), "/tests/assets/" ); - - -fn arrange( source: &str ) -> assert_fs::TempDir -{ - let root_path = Path::new( env!( "CARGO_MANIFEST_DIR" ) ); - let assets_relative_path = Path::new( ASSET_PATH ); - let assets_path = root_path.join( assets_relative_path ); - - let temp = assert_fs::TempDir::new().unwrap(); - temp.copy_from( assets_path.join( source ), &[ "**" ] ).unwrap(); - - temp -} pub fn start_sync< AP, Args, Arg, P > ( application : AP, @@ -29,14 +14,15 @@ pub fn start_sync< AP, Args, Arg, P > let args = args.into_iter().map( | a | a.as_ref().into() ).collect::< Vec< std::ffi::OsString > >(); let child = Command::new( application ).args( &args ).stdout( Stdio::piped() ).stderr( Stdio::piped() ).current_dir( path ).spawn().unwrap(); let output = child.wait_with_output().unwrap(); - dbg!( &output ); - + String::from_utf8( output.stdout ).unwrap() } #[ test ] fn help_command_with_optional_params() { + let temp = assert_fs::TempDir::new().unwrap(); + let toml = format! ( r#"[package] @@ -47,15 +33,164 @@ edition = "2021" wca = {{path = "{}"}}"#, env!( "CARGO_MANIFEST_DIR" ).replace( "\\", "/" ) ) ; - - let temp = arrange( "wca_hello_test" ); - let mut file = File::create( temp.path().join( "Cargo.toml" ) ).unwrap(); - file.write_all( toml.as_bytes() ).unwrap(); + + let main = r#"use wca::{ Type, VerifiedCommand }; + fn main(){ + let ca = wca::CommandsAggregator::former() + .command( "echo" ) + .hint( "prints all subjects and properties" ) + .subject().hint( "Subject" ).kind( Type::String ).optional( true ).end() + .property( "property" ).hint( "simple property" ).kind( Type::String ).optional( true ).end() + .routine( | o : VerifiedCommand | { println!( "= Args\n{:?}\n\n= Properties\n{:?}\n", o.args, o.props ) } ) + .end() + .perform(); + + let args = std::env::args().skip( 1 ).collect::< Vec< String > >(); + ca.perform( args ).unwrap(); + } + "#; + File::create( temp.path().join( "Cargo.toml" ) ).unwrap().write_all( toml.as_bytes() ).unwrap(); + DirBuilder::new().create( temp.join( "src" ) ).unwrap(); + File::create( temp.path().join( "src" ).join( "main.rs" ) ).unwrap().write_all( main.as_bytes() ).unwrap(); let result = start_sync( "cargo", [ "r", ".help", "echo" ], temp.path() ); - assert_eq! ( "Help command\n\n.echo < subjects > < properties > - prints all subjects and properties\n\nSubjects:\n\t- Subject [?String]\nProperties:\n\tproperty - simple property [?String]\n", result ); } + +#[ test ] +fn help_command_with_nature_order() +{ + let temp = assert_fs::TempDir::new().unwrap(); + + let toml = format! + ( + r#"[package] +name = "wca_hello_test" +version = "0.1.0" +edition = "2021" +[dependencies] +wca = {{path = "{}"}}"#, + env!( "CARGO_MANIFEST_DIR" ).replace( "\\", "/" ) + ) ; + + let main = r#"fn main() + { + use wca::{ Type, VerifiedCommand, Order }; + + let ca = wca::CommandsAggregator::former() + .command( "c" ) + .hint( "c" ) + .property( "c-property" ).kind( Type::String ).optional( true ).end() + .property( "b-property" ).kind( Type::String ).optional( true ).end() + .property( "a-property" ).kind( Type::String ).optional( true ).end() + .routine( | o : VerifiedCommand | { println!("c") } ) + .end() + .command( "b" ) + .hint( "b" ) + .property( "b-property" ).kind( Type::String ).optional( true ).end() + .routine( | o : VerifiedCommand | { println!("b") } ) + .end() + .command( "a" ) + .hint( "a" ) + .property( "a-property" ).kind( Type::String ).optional( true ).end() + .routine( | o : VerifiedCommand | { println!("a") } ) + .end() + .order( Order::Nature ) + + .perform(); + + let args = std::env::args().skip( 1 ).collect::< Vec< String > >(); + ca.perform( args ).unwrap(); + }"#; + + File::create( temp.path().join( "Cargo.toml" ) ).unwrap().write_all( toml.as_bytes() ).unwrap(); + DirBuilder::new().create( temp.join( "src" ) ).unwrap(); + File::create( temp.path().join( "src" ).join( "main.rs" ) ).unwrap().write_all( main.as_bytes() ).unwrap(); + + let result = start_sync( "cargo", [ "r", ".help" ], temp.path() ); + + assert_eq! + ( + "Help command\n\n.c - c\n.b - b\n.a - a\n", + result + ); + + let result = start_sync( "cargo", [ "r", ".help", "c" ], temp.path() ); + + println!( "{result}" ); + + assert_eq! + ( + "Help command\n\n.c - c\n\nProperties:\n\tc-property - [?String]\n\tb-property - [?String]\n\ta-property - [?String]\n", + result + ); +} + +#[ test ] +fn help_command_with_lexicography_order() +{ + let temp = assert_fs::TempDir::new().unwrap(); + + let toml = format! + ( + r#"[package] +name = "wca_hello_test" +version = "0.1.0" +edition = "2021" +[dependencies] +wca = {{path = "{}"}}"#, + env!( "CARGO_MANIFEST_DIR" ).replace( "\\", "/" ) + ) ; + + let main = r#"fn main() + { + use wca::{ Type, VerifiedCommand, Order }; + + let ca = wca::CommandsAggregator::former() + .command( "c" ) + .hint( "c" ) + .property( "c-property" ).kind( Type::String ).optional( true ).end() + .property( "b-property" ).kind( Type::String ).optional( true ).end() + .property( "a-property" ).kind( Type::String ).optional( true ).end() + .routine( | o : VerifiedCommand | { println!("c") } ) + .end() + .command( "b" ) + .hint( "b" ) + .property( "b-property" ).kind( Type::String ).optional( true ).end() + .routine( | o : VerifiedCommand | { println!("b") } ) + .end() + .command( "a" ) + .hint( "a" ) + .property( "a-property" ).kind( Type::String ).optional( true ).end() + .routine( | o : VerifiedCommand | { println!("a") } ) + .end() + .order( Order::Lexicography ) + .perform(); + + let args = std::env::args().skip( 1 ).collect::< Vec< String > >(); + ca.perform( args ).unwrap(); + }"#; + + File::create( temp.path().join( "Cargo.toml" ) ).unwrap().write_all( toml.as_bytes() ).unwrap(); + DirBuilder::new().create( temp.join( "src" ) ).unwrap(); + File::create( temp.path().join( "src" ).join( "main.rs" ) ).unwrap().write_all( main.as_bytes() ).unwrap(); + + let result = start_sync( "cargo", [ "r", ".help" ], temp.path() ); + + assert_eq! + ( + "Help command\n\n.a - a\n.b - b\n.c - c\n", + result + ); + + let result = start_sync( "cargo", [ "r", ".help", "c" ], temp.path() ); + + assert_eq! + ( + "Help command\n\n.c - c\n\nProperties:\n\ta-property - [?String]\n\tb-property - [?String]\n\tc-property - [?String]\n", + result + ); +} diff --git a/module/move/willbe/Cargo.toml b/module/move/willbe/Cargo.toml index 3ceeabea66..27fed31983 100644 --- a/module/move/willbe/Cargo.toml +++ b/module/move/willbe/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "willbe" -version = "0.9.0" +version = "0.11.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", diff --git a/module/move/willbe/src/action/cicd_renew.rs b/module/move/willbe/src/action/cicd_renew.rs index 999d993998..573a2a13bc 100644 --- a/module/move/willbe/src/action/cicd_renew.rs +++ b/module/move/willbe/src/action/cicd_renew.rs @@ -166,11 +166,16 @@ mod private file_write( &workflow_root.join( "standard_rust_push.yml" ), include_str!( "../../template/workflow/standard_rust_push.yml" ) )?; + file_write( &workflow_root.join( "for_pr_rust_push.yml" ), include_str!( "../../template/workflow/for_pr_rust_push.yml" ) )?; + file_write( &workflow_root.join( "standard_rust_scheduled.yml" ), include_str!( "../../template/workflow/standard_rust_scheduled.yml" ) )?; file_write( &workflow_root.join( "standard_rust_status.yml" ), include_str!( "../../template/workflow/standard_rust_status.yml" ) )?; file_write( &workflow_root.join( "status_checks_rules_update.yml" ), include_str!( "../../template/workflow/status_checks_rules_update.yml" ) )?; + + file_write( &workflow_root.join( "Readme.md" ), include_str!( "../../template/workflow/Readme.md" ) )?; + Ok( () ) } diff --git a/module/move/willbe/src/action/features.rs b/module/move/willbe/src/action/features.rs new file mode 100644 index 0000000000..47b9e98f44 --- /dev/null +++ b/module/move/willbe/src/action/features.rs @@ -0,0 +1,97 @@ +mod private +{ + use crate::*; + + use std:: + { + collections::{ BTreeMap, HashMap }, + fmt + }; + + use _path::AbsolutePath; + use former::Former; + use error_tools::{ for_app::Context, Result }; + use workspace::Workspace; + + /// Options available for the .features command + #[ derive( Debug, Former ) ] + pub struct FeaturesOptions + { + manifest_dir : AbsolutePath, + with_features_deps : bool, + } + + /// Represents a report about features available in the package + #[ derive( Debug, Default ) ] + pub struct FeaturesReport + { + /// Flag to turn off/on displaying feature dependencies - "feature: [deps...]" + pub with_features_deps : bool, + + /// A key-value pair structure representing available features. + /// + /// Key: name of the package (useful for workspaces, where multiple packages can be found). + /// + /// Value: Another key-value pair representing a feature and its dependencies + pub inner : HashMap< String, BTreeMap< String, Vec< String > > >, + } + + impl fmt::Display for FeaturesReport + { + fn fmt( &self, f : &mut fmt::Formatter< '_ >) -> Result< (), fmt::Error > + { + self.inner.iter().try_for_each + ( | ( package, features ) | + { + writeln!(f, "Package {}:", package)?; + features.iter().try_for_each + ( | ( feature, dependencies ) | + { + let feature = match self.with_features_deps + { + false => format!( "\t{feature}" ), + true + => + { + let deps = dependencies.join( ", " ); + format!( "\t{feature}: [{deps}]" ) + } + }; + writeln!( f, "{feature}" ) + } + ) + } + ) + } + } + + /// List features + pub fn features( FeaturesOptions { manifest_dir, with_features_deps } : FeaturesOptions ) -> Result< FeaturesReport > + { + let workspace = Workspace::with_crate_dir( CrateDir::try_from( manifest_dir.clone() )? ).context( "Failed to find workspace" )?; + let packages = workspace.packages()?.into_iter().filter + ( | package | + package.manifest_path().as_str().starts_with( manifest_dir.as_ref().as_os_str().to_str().unwrap() ) + ).collect::< Vec< _ > >(); + let mut report = FeaturesReport + { + with_features_deps, + ..Default::default() + }; + packages.iter().for_each + ( | package | + { + let features = package.features(); + report.inner.insert(package.name().to_owned(), features.to_owned()); + } + ); + Ok( report ) + } +} + +crate::mod_interface! +{ + orphan use features; + orphan use FeaturesOptions; + orphan use FeaturesReport; +} diff --git a/module/move/willbe/src/action/main_header.rs b/module/move/willbe/src/action/main_header.rs index 80ec04b352..e1b5a7a16e 100644 --- a/module/move/willbe/src/action/main_header.rs +++ b/module/move/willbe/src/action/main_header.rs @@ -1,6 +1,7 @@ mod private { use crate::*; + use std::fmt::{ Display, Formatter }; use std::fs:: { OpenOptions @@ -12,9 +13,8 @@ mod private SeekFrom, Write }; + use std::path::PathBuf; use regex::Regex; - use wtools::error::err; - use error_tools::Result; use wca::wtools::anyhow::Error; use action::readme_health_table_renew:: { @@ -23,11 +23,24 @@ mod private }; use _path::AbsolutePath; use { CrateDir, query, url, Workspace, wtools }; - use wtools::error::anyhow:: + use entity::{ CrateDirError, WorkspaceError }; + use wtools::error:: { - format_err + anyhow::format_err, + err, + for_app:: + { + Result, + Error as wError, + Context, + }, }; - + use error_tools:: + { + dependency::*, + for_lib::Error, + }; + static TAGS_TEMPLATE : std::sync::OnceLock< Regex > = std::sync::OnceLock::new(); fn regexes_initialize() @@ -35,6 +48,58 @@ mod private TAGS_TEMPLATE.set( Regex::new( r"(.|\n|\r\n)+" ).unwrap() ).ok(); } + /// Report. + #[ derive( Debug, Default, Clone ) ] + pub struct MainHeaderRenewReport + { + found_file : Option< PathBuf >, + touched_file : PathBuf, + success : bool, + } + + impl Display for MainHeaderRenewReport + { + fn fmt( &self, f : &mut Formatter< '_ > ) -> std::fmt::Result + { + if self.success + { + if let Some( file_path ) = self.touched_file.to_str() + { + writeln!( f, "File successful changed : {file_path}." )?; + } + else + { + writeln!( f, "File successful changed but contains non-UTF-8 characters." )?; + } + } + else + { + if let Some( Some( file_path ) ) = self.found_file.as_ref().map( | p | p.to_str() ) + { + writeln!( f, "File found but not changed : {file_path}." )?; + } + else + { + writeln!( f, "File not found or contains non-UTF-8 characters." )?; + } + } + Ok( () ) + } + } + + #[ derive( Debug, Error ) ] + pub enum MainHeaderRenewError + { + #[ error( "Common error: {0}" ) ] + Common(#[ from ] wError ), + #[ error( "I/O error: {0}" ) ] + IO( #[ from ] std::io::Error ), + #[ error( "Workspace error: {0}" ) ] + Workspace( #[ from ] WorkspaceError), + #[ error( "Directory error: {0}" ) ] + Directory( #[ from ] CrateDirError ), + } + /// The `HeaderParameters` structure represents a set of parameters, used for creating url for header. struct HeaderParameters { @@ -47,7 +112,7 @@ mod private impl HeaderParameters { /// Create `HeaderParameters` instance from the folder where Cargo.toml is stored. - fn from_cargo_toml( workspace : Workspace ) -> Result< Self > + fn from_cargo_toml( workspace : Workspace ) -> Result< Self, MainHeaderRenewError > { let repository_url = workspace.repository_url()?.ok_or_else::< Error, _ >( || err!( "repo_url not found in workspace Cargo.toml" ) )?; let master_branch = workspace.master_branch()?.unwrap_or( "master".into() ); @@ -67,7 +132,7 @@ mod private } /// Convert `Self`to header. - fn to_header( self ) -> Result< String > + fn to_header( self ) -> Result< String, MainHeaderRenewError > { let discord = self.discord_url.map( | discord | format!( "\n[![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)]({discord})" ) @@ -78,12 +143,12 @@ mod private ( format! ( - r#"[![{}](https://img.shields.io/github/actions/workflow/status/{}/auto_merge_to_beta.yml?branch=master&label={}&logo=github)](https://github.com/{}/actions/workflows/auto_merge_to_beta.yml){} -[![Open in Gitpod](https://raster.shields.io/static/v1?label=try&message=online&color=eee&logo=gitpod&logoColor=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=sample%2Frust%2F{}_trivial%2Fsrc%2Fmain.rs,RUN_POSTFIX=--example%20{}_trivial/https://github.com/{}) + r#"[![{}](https://img.shields.io/github/actions/workflow/status/{}/standard_rust_scheduled.yml?label={}&logo=github&branch={})](https://github.com/{}/actions/workflows/standard_rust_scheduled.yml){} +[![Open in Gitpod](https://raster.shields.io/static/v1?label=try&message=online&color=eee&logo=gitpod&logoColor=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=sample%2Frust%2F{}_trivial_sample%2Fsrc%2Fmain.rs,RUN_POSTFIX=--example%20{}_trivial_sample/https://github.com/{}) [![docs.rs](https://raster.shields.io/static/v1?label=docs&message=online&color=eee&logo=docsdotrs&logoColor=eee)](https://docs.rs/{})"#, - self.master_branch, url::git_info_extract( &self.repository_url )?, self.master_branch, url::git_info_extract( &self.repository_url )?, + self.workspace_name, url::git_info_extract( &self.repository_url )?, self.workspace_name, self.master_branch, url::git_info_extract( &self.repository_url )?, discord, - self.workspace_name, self.workspace_name, url::git_info_extract( &self.repository_url )?, + self.workspace_name.to_lowercase(), self.workspace_name.to_lowercase(), url::git_info_extract( &self.repository_url )?, self.workspace_name, ) ) @@ -113,21 +178,40 @@ mod private /// [![docs.rs](https://raster.shields.io/static/v1?label=docs&message=online&color=eee&logo=docsdotrs&logoColor=eee)](https://docs.rs/wtools) /// /// ``` - pub fn readme_header_renew( path : AbsolutePath ) -> Result< () > + pub fn readme_header_renew( path : AbsolutePath ) -> Result< MainHeaderRenewReport, ( MainHeaderRenewReport, MainHeaderRenewError ) > { + let mut report = MainHeaderRenewReport::default(); regexes_initialize(); - let mut cargo_metadata = Workspace::with_crate_dir( CrateDir::try_from( path )? )?; - let workspace_root = workspace_root( &mut cargo_metadata )?; - let header_param = HeaderParameters::from_cargo_toml( cargo_metadata )?; - let read_me_path = workspace_root.join( readme_path( &workspace_root ).ok_or_else( || format_err!( "Fail to find README.md" ) )?); + let mut cargo_metadata = Workspace::with_crate_dir + ( + CrateDir::try_from( path ) + .map_err( | e | ( report.clone(), e.into() ) )? + ).map_err( | e | ( report.clone(), e.into() ) )?; + + let workspace_root = workspace_root( &mut cargo_metadata ) + .map_err( | e | ( report.clone(), e.into() ) )?; + + let header_param = HeaderParameters::from_cargo_toml( cargo_metadata ) + .map_err( | e | ( report.clone(), e.into() ) )?; + + let read_me_path = workspace_root.join + ( + readme_path( &workspace_root ) + .ok_or_else( || format_err!( "Fail to find README.md" ) ) + .map_err( | e | ( report.clone(), e.into() ) )? + ); + + report.found_file = Some( read_me_path.clone() ); + let mut file = OpenOptions::new() .read( true ) .write( true ) - .open( &read_me_path )?; + .open( &read_me_path ) + .map_err( | e | ( report.clone(), e.into() ) )?; let mut content = String::new(); - file.read_to_string( &mut content )?; + file.read_to_string( &mut content ).map_err( | e | ( report.clone(), e.into() ) )?; let raw_params = TAGS_TEMPLATE .get() @@ -137,14 +221,21 @@ mod private .map( | m | m.as_str() ) .unwrap_or_default(); - _ = query::parse( raw_params )?; + _ = query::parse( raw_params ).context( "Fail to parse arguments" ); - let header = header_param.to_header()?; - let content : String = TAGS_TEMPLATE.get().unwrap().replace( &content, &format!( "\n{header}\n" ) ).into(); - file.set_len( 0 )?; - file.seek( SeekFrom::Start( 0 ) )?; - file.write_all( content.as_bytes() )?; - Ok( () ) + let header = header_param.to_header().map_err( | e | ( report.clone(), e.into() ) )?; + let content : String = TAGS_TEMPLATE.get().unwrap().replace + ( + &content, + &format!( "\n{header}\n" ) + ).into(); + + file.set_len( 0 ).map_err( | e | ( report.clone(), e.into() ) )?; + file.seek( SeekFrom::Start( 0 ) ).map_err( | e | ( report.clone(), e.into() ) )?; + file.write_all( content.as_bytes() ).map_err( | e | ( report.clone(), e.into() ) )?; + report.touched_file = read_me_path; + report.success = true; + Ok( report ) } } @@ -152,4 +243,6 @@ crate::mod_interface! { /// Generate header. orphan use readme_header_renew; + /// Report. + orphan use MainHeaderRenewReport; } \ No newline at end of file diff --git a/module/move/willbe/src/action/mod.rs b/module/move/willbe/src/action/mod.rs index 03d817fc44..0c66bb41ce 100644 --- a/module/move/willbe/src/action/mod.rs +++ b/module/move/willbe/src/action/mod.rs @@ -22,4 +22,6 @@ crate::mod_interface! layer cicd_renew; /// Workspace new. layer workspace_renew; + /// List features. + layer features; } diff --git a/module/move/willbe/src/action/publish.rs b/module/move/willbe/src/action/publish.rs index 69ce35400d..6788a11f1c 100644 --- a/module/move/willbe/src/action/publish.rs +++ b/module/move/willbe/src/action/publish.rs @@ -37,7 +37,6 @@ mod private for ( path, report ) in &self.packages { let report = report.to_string().replace("\n", "\n "); - // qqq : remove unwrap let path = if let Some( wrd ) = &self.workspace_root_dir { path.as_ref().strip_prefix( &wrd.as_ref() ).unwrap() @@ -77,13 +76,13 @@ mod private { if let Some( pos ) = actually_published.iter().position( | p | p == &path ) { - write!( f, "✅ {name} {}", version.new_version )?; + writeln!( f, "✅ {name} {}", version.new_version )?; // want to check that only expected packages actually published _ = actually_published.remove( pos ); } else { - write!( f, "❌ {name} {}", version.old_version )?; + writeln!( f, "❌ {name} {}", version.old_version )?; } } if !actually_published.is_empty() @@ -172,7 +171,7 @@ mod private let queue = graph::toposort( subgraph ).unwrap().into_iter().map( | n | package_map.get( &n ).unwrap() ).cloned().collect::< Vec< _ > >(); let roots = packages_to_publish.iter().map( | p | package_map.get( p ).unwrap().crate_dir() ).collect::< Vec< _ > >(); - + let plan = package::PublishPlan::former() .workspace_dir( CrateDir::try_from( workspace_root_dir ).unwrap() ) .option_base_temp_dir( dir.clone() ) @@ -180,7 +179,7 @@ mod private .roots( roots ) .packages( queue ) .form(); - + Ok( plan ) } diff --git a/module/move/willbe/src/action/readme_modules_headers_renew.rs b/module/move/willbe/src/action/readme_modules_headers_renew.rs index e3acf34272..5809369b10 100644 --- a/module/move/willbe/src/action/readme_modules_headers_renew.rs +++ b/module/move/willbe/src/action/readme_modules_headers_renew.rs @@ -2,20 +2,31 @@ mod private { use crate::*; use _path::AbsolutePath; - use action::readme_health_table_renew::{ readme_path, Stability, stability_generate }; + use action::readme_health_table_renew::{ readme_path, Stability, stability_generate, find_example_file }; use package::Package; use wtools::error:: { err, - for_app::{ Result, Error }, + for_app:: + { + Result, + Error as wError, + Context, + }, }; use std::borrow::Cow; + use std::collections::BTreeSet; + use std::fmt::{Display, Formatter}; use std::fs::{ OpenOptions }; use std::io::{ Read, Seek, SeekFrom, Write }; use std::path::PathBuf; use convert_case::{ Case, Casing }; use regex::Regex; - use crate::action::readme_health_table_renew::find_example_file; + use entity::WorkspaceError; + use manifest::private::CrateDirError; + use package::PackageError; + use error_tools::for_lib::Error; + use error_tools::dependency::*; // aaa : for Petro : rid off crate::x. ask // aaa : add `use crate::*` first @@ -26,6 +37,56 @@ mod private TAGS_TEMPLATE.set( Regex::new( r"(.|\n|\r\n)+" ).unwrap() ).ok(); } + /// Report. + #[ derive( Debug, Default, Clone ) ] + pub struct ModulesHeadersRenewReport + { + found_files : BTreeSet< PathBuf >, + touched_files : BTreeSet< PathBuf >, + } + + impl Display for ModulesHeadersRenewReport + { + fn fmt( &self, f : &mut Formatter< '_ > ) -> std::fmt::Result + { + if self.touched_files.len() < self.found_files.len() + { + writeln!( f, "Something went wrong.\n{}/{} was touched.", self.found_files.len(), self.touched_files.len() )?; + return Ok(()) + } + writeln!( f, "Touched files :" )?; + let mut count = self.found_files.len(); + for path in &self.touched_files + { + if let Some( file_path ) = path.to_str() + { + writeln!( f, "{file_path}" )?; + count -= 1; + } + } + if count != 0 + { + writeln!( f, "Other {count} files contains non-UTF-8 characters." )?; + } + Ok( () ) + } + } + + #[ derive( Debug, Error ) ] + pub enum ModulesHeadersRenewError + { + #[ error( "Common error: {0}" ) ] + Common(#[ from ] wError ), + #[ error( "I/O error: {0}" ) ] + IO( #[ from ] std::io::Error ), + #[ error( "Workspace error: {0}" ) ] + Workspace( #[ from ] WorkspaceError), + #[ error( "Package error: {0}" ) ] + Package( #[ from ] PackageError), + #[ error( "Directory error: {0}" ) ] + Directory( #[ from ] CrateDirError ), + } + /// The `ModuleHeader` structure represents a set of parameters, used for creating url for header. struct ModuleHeader { @@ -40,13 +101,13 @@ mod private { /// Create `ModuleHeader` instance from the folder where Cargo.toml is stored. - fn from_cargo_toml( package : Package, default_discord_url : &Option< String > ) -> Result< Self > + fn from_cargo_toml( package : Package, default_discord_url : &Option< String > ) -> Result< Self, ModulesHeadersRenewError > { let stability = package.stability()?; let module_name = package.name()?; - let repository_url = package.repository()?.ok_or_else::< Error, _ >( || err!( "Fail to find repository_url in module`s Cargo.toml" ) )?; + let repository_url = package.repository()?.ok_or_else::< wError, _ >( || err!( "Fail to find repository_url in module`s Cargo.toml" ) )?; let discord_url = package.discord_url()?.or_else( || default_discord_url.clone() ); Ok @@ -63,17 +124,19 @@ mod private } /// Convert `ModuleHeader`to header. - fn to_header( self, workspace_path : &str ) -> Result< String > + fn to_header( self, workspace_path : &str ) -> Result< String, ModulesHeadersRenewError > { let discord = self.discord_url.map( | discord_url | format!( " [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)]({discord_url})" ) ) .unwrap_or_default(); - let repo_url = url::extract_repo_url( &self.repository_url ).and_then( | r | url::git_info_extract( &r ).ok() ).ok_or_else::< Error, _ >( || err!( "Fail to parse repository url" ) )?; + let repo_url = url::extract_repo_url( &self.repository_url ).and_then( | r | url::git_info_extract( &r ).ok() ).ok_or_else::< wError, _ >( || err!( "Fail to parse repository url" ) )?; let example = if let Some( name ) = find_example_file( self.module_path.as_path(), &self.module_name ) { + // qqq : for Petro : Hardcoded Strings, would be better to use `PathBuf` to avoid separator mismatch on Windows and Unix let p = name.strip_prefix( workspace_path ).unwrap().get( 1.. ).unwrap().replace( "\\","%2F" ); + let name = name.replace( "/", "\\" ); let name = name.split( "\\" ).last().unwrap().split( "." ).next().unwrap(); format!( " [![Open in Gitpod](https://raster.shields.io/static/v1?label=try&message=online&color=eee&logo=gitpod&logoColor=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE={p},RUN_POSTFIX=--example%20{}/https://github.com/{})", name, repo_url ) } @@ -117,28 +180,32 @@ mod private /// [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://github.com/Username/test/actions/workflows/ModuleChainOfPackagesAPush.yml/badge.svg)](https://github.com/Username/test/actions/workflows/ModuleChainOfPackagesAPush.yml)[![docs.rs](https://img.shields.io/docsrs/_chain_of_packages_a?color=e3e8f0&logo=docs.rs)](https://docs.rs/_chain_of_packages_a)[![Open in Gitpod](https://raster.shields.io/static/v1?label=try&message=online&color=eee&logo=gitpod&logoColor=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=sample%2Frust%2F_chain_of_packages_a_trivial%2Fsrc%2Fmain.rs,RUN_POSTFIX=--example%20_chain_of_packages_a_trivial/https://github.com/Username/test) /// /// ``` - pub fn readme_modules_headers_renew( path : AbsolutePath ) -> Result< () > + pub fn readme_modules_headers_renew( path : AbsolutePath ) -> Result< ModulesHeadersRenewReport, ( ModulesHeadersRenewReport, ModulesHeadersRenewError ) > { + let mut report = ModulesHeadersRenewReport::default(); regexes_initialize(); - let cargo_metadata = Workspace::with_crate_dir( CrateDir::try_from( path )? )?; - let discord_url = cargo_metadata.discord_url()?; - for path in cargo_metadata.packages()?.into_iter().filter_map( | p | AbsolutePath::try_from( p.manifest_path() ).ok()) + let cargo_metadata = Workspace::with_crate_dir( CrateDir::try_from( path ).map_err( | e | ( report.clone(), e.into() ) )? ).map_err( | e | ( report.clone(), e.into() ) )?; + let discord_url = cargo_metadata.discord_url().map_err( | e | ( report.clone(), e.into() ) )?; + let paths = cargo_metadata.packages().map_err( | e | ( report.clone(), e.into() ) )?.into_iter().filter_map( | p | AbsolutePath::try_from( p.manifest_path() ).ok()).collect::< Vec< _ > >(); + report.found_files = paths.iter().map( | ap | ap.as_ref().to_path_buf() ).collect(); + for path in paths { let read_me_path = path .parent() .unwrap() - .join( readme_path( path.parent().unwrap().as_ref() ).ok_or_else::< Error, _ >( || err!( "Fail to find README.md" ) )? ); + .join( readme_path( path.parent().unwrap().as_ref() ).ok_or_else::< wError, _ >( || err!( "Fail to find README.md" ) ).map_err( | e | ( report.clone(), e.into() ) )? ); - let pakage = Package::try_from( path )?; - let header = ModuleHeader::from_cargo_toml( pakage, &discord_url )?; + let pakage = Package::try_from( path.clone() ).map_err( | e | ( report.clone(), e.into() ) )?; + let header = ModuleHeader::from_cargo_toml( pakage.into(), &discord_url ).map_err( | e | ( report.clone(), e.into() ) )?; let mut file = OpenOptions::new() .read( true ) .write( true ) - .open( &read_me_path )?; + .open( &read_me_path ) + .map_err( | e | ( report.clone(), e.into() ) )?; let mut content = String::new(); - file.read_to_string( &mut content )?; + file.read_to_string( &mut content ).map_err( | e | ( report.clone(), e.into() ) )?; let raw_params = TAGS_TEMPLATE .get() @@ -148,15 +215,16 @@ mod private .map( | m | m.as_str() ) .unwrap_or_default(); - _ = query::parse( raw_params )?; + _ = query::parse( raw_params ).context( "Fail to parse raw params." ); - let content = header_content_generate( &content, header, raw_params, cargo_metadata.workspace_root()?.to_str().unwrap() )?; + let content = header_content_generate( &content, header, raw_params, cargo_metadata.workspace_root().map_err( | e | ( report.clone(), e.into() ) )?.to_str().unwrap() ).map_err( | e | ( report.clone(), e.into() ) )?; - file.set_len( 0 )?; - file.seek( SeekFrom::Start( 0 ) )?; - file.write_all( content.as_bytes() )?; + file.set_len( 0 ).map_err( | e | ( report.clone(), e.into() ) )?; + file.seek( SeekFrom::Start( 0 ) ).map_err( | e | ( report.clone(), e.into() ) )?; + file.write_all( content.as_bytes() ).map_err( | e | ( report.clone(), e.into() ) )?; + report.touched_files.insert( path.as_ref().to_path_buf() ); } - Ok( () ) + Ok( report ) } fn header_content_generate< 'a >( content : &'a str, header : ModuleHeader, raw_params : &str, workspace_root : &str ) -> Result< Cow< 'a, str > > @@ -171,4 +239,6 @@ crate::mod_interface! { /// Generate headers in modules orphan use readme_modules_headers_renew; + /// report + orphan use ModulesHeadersRenewReport; } \ No newline at end of file diff --git a/module/move/willbe/src/action/test.rs b/module/move/willbe/src/action/test.rs index 72483c71d6..7ad9a532a4 100644 --- a/module/move/willbe/src/action/test.rs +++ b/module/move/willbe/src/action/test.rs @@ -8,38 +8,9 @@ mod private use std::collections::HashSet; use std::{ env, fs }; - // qqq : for Petro : https://github.com/obox-systems/conventions/blob/master/code_style.md#importing-structuring-std-imports #[ cfg( feature = "progress_bar" ) ] use indicatif::{ MultiProgress, ProgressStyle }; - // aaa : for Petro : don't use cargo_metadata and Package directly, use facade - // aaa : ✅ - - // qqq : for Petro : don't use Package directly. rid it off for the whole willbe - - // qqq : for Petro : should not be such combinations full,no_std - // [ release | nightly | full,no_std ]: ❌ failed - - // qqq : for Petro : improve formatting - // - // [ optimization : debug | channel : stable | feature : derive_component_from,use_alloc ] - // [ optimization : debug | channel : stable | feature : default,enabled ] - // [ optimization : debug | channel : stable | feature : derive_components_assign ] - // [ optimization : debug | channel : stable | feature : derive_component_from,derive_component_assign ] - // [ optimization : debug | channel : stable | feature : derive_former,derive_component_assign ] - // [ optimization : debug | channel : stable | feature : enabled ] - // [ optimization : debug | channel : stable | feature : derive_component_assign,no_std ] - // [ optimization : debug | channel : stable | feature : default,derive_component_assign ] - // [ optimization : debug | channel : stable | feature : no-features ] - // - // should be - // - // [ optimization : release | channel : nightly | feature : full ] -> [ optimization : release | channel : nightly | feature : [ list all features ] ] - // [ optimization : debug | channel : stable | feature : [] ] - // - // don't create artifical categories as no-features - // - // make table out of that use former::Former; use wtools:: @@ -89,7 +60,7 @@ mod private #[ former( default = false ) ] with_progress : bool, } - + /// The function runs tests with a different set of features in the selected crate (the path to the crate is specified in the dir variable). /// Tests are run with each feature separately, with all features together, and without any features. @@ -131,7 +102,7 @@ mod private with_all_features, with_none_features, optimizations, - variants_cap, + variants_cap, with_progress, } = args; @@ -189,12 +160,12 @@ mod private #[ cfg( feature = "progress_bar" ) ] let test_options_former = if with_progress - { + { let test_options_former = test_options_former.feature( TestOptionsProgressBarFeature{ multiprocess, style } ); test_options_former } else - { + { test_options_former }; diff --git a/module/move/willbe/src/command/features.rs b/module/move/willbe/src/command/features.rs new file mode 100644 index 0000000000..ad69897935 --- /dev/null +++ b/module/move/willbe/src/command/features.rs @@ -0,0 +1,40 @@ +mod private +{ + use crate::*; + + use action::features::FeaturesOptions; + use std::path::PathBuf; + use _path::AbsolutePath; + use wca::VerifiedCommand; + use wtools::error::Result; + + /// + /// List features of a package. + /// + + pub fn features( o : VerifiedCommand ) -> Result< () > + { + let path : PathBuf = o.args.get_owned( 0 ).unwrap_or_else( || "./".into() ); + let path = AbsolutePath::try_from( path )?; + let with_features_deps = o.props.get_owned( "with_features_deps" ).unwrap_or( false ); + let options = FeaturesOptions::former() + .manifest_dir( path ) + .with_features_deps( with_features_deps ) + .form(); + let report = action::features( options ); + match report + { + Ok(success) => println!("{success}"), + Err(failure) => eprintln!("{failure}"), + } + Ok( () ) + } + +} + +crate::mod_interface! +{ + /// List features. + orphan use features; +} + diff --git a/module/move/willbe/src/command/list.rs b/module/move/willbe/src/command/list.rs index 9c970783f9..154317904c 100644 --- a/module/move/willbe/src/command/list.rs +++ b/module/move/willbe/src/command/list.rs @@ -19,9 +19,8 @@ mod private use action::{ list as l, list::{ ListFormat, ListOptions } }; use former::Former; - // qqq: `Former` forces the struct to be public #[ derive( Former ) ] - pub struct ListProperties + struct ListProperties { #[ former( default = ListFormat::Tree ) ] format : ListFormat, diff --git a/module/move/willbe/src/command/main_header.rs b/module/move/willbe/src/command/main_header.rs index a9ddd22743..558f2a625b 100644 --- a/module/move/willbe/src/command/main_header.rs +++ b/module/move/willbe/src/command/main_header.rs @@ -3,12 +3,25 @@ mod private use crate::*; use action; use _path::AbsolutePath; - use error_tools::{ for_app::Context, Result }; + use error_tools::Result; + use wtools::error::anyhow::Error; /// Generates header to main Readme.md file. pub fn readme_header_renew() -> Result< () > { - action::readme_header_renew( AbsolutePath::try_from( std::env::current_dir()? )? ).context( "Fail to create table" ) + match action::readme_header_renew( AbsolutePath::try_from( std::env::current_dir()? )? ) + { + Ok( report ) => + { + println!( "{report}" ); + Ok( () ) + } + Err( ( report, e ) ) => + { + eprintln!( "{report}" ); + Err( Error::from( e ).context( "Fail to generate main header." ) ) + } + } } } diff --git a/module/move/willbe/src/command/mod.rs b/module/move/willbe/src/command/mod.rs index d3d5e5950b..74f58a48bc 100644 --- a/module/move/willbe/src/command/mod.rs +++ b/module/move/willbe/src/command/mod.rs @@ -196,7 +196,6 @@ with_gitpod: If set to 1, a column with a link to Gitpod will be added. Clicking .routine( command::test ) .end() - // qqq : is it right? .command( "cicd.renew" ) .hint( "generate a CI/CD for the workspace" ) .long_hint( "this command generates a development workflow for the entire workspace inferred from the current directory. The workflow outlines the build steps, dependencies, test processes, and more for all modules within the workspace." ) @@ -247,15 +246,37 @@ with_gitpod: If set to 1, a column with a link to Gitpod will be added. Clicking .command( "readme.header.renew" ) .hint( "Generate header in workspace`s Readme.md file") - .long_hint( "For use this command you need to specify:\n\n[workspace.metadata]\nmaster_branch = \"alpha\"\nworkspace_name = \"wtools\"\nrepo_url = \"https://github.com/Wandalen/wTools\"\ndiscord_url = \"https://discord.gg/123123\"\n\nin workspace's Cargo.toml.") + .long_hint( "Generate header which contains a badge with the general status of workspace, a link to discord, an example in gitpod and documentation in workspace`s Readme.md file.\n For use this command you need to specify:\n\n[workspace.metadata]\nmaster_branch = \"alpha\"\nworkspace_name = \"wtools\"\nrepo_url = \"https://github.com/Wandalen/wTools\"\ndiscord_url = \"https://discord.gg/123123\"\n\nin workspace's Cargo.toml.") .routine( command::readme_header_renew ) .end() .command( "readme.modules.headers.renew" ) .hint( "Generates header for each workspace member." ) - .long_hint( "For use this command you need to specify:\n\n[package]\nname = \"test_module\"\nrepository = \"https://github.com/Username/ProjectName/tree/master/module/test_module\"\n...\n[package.metadata]\nstability = \"stable\" (Optional)\ndiscord_url = \"https://discord.gg/1234567890\" (Optional)\n\nin module's Cargo.toml." ) + .long_hint( "Generates header for each workspace member which contains a badge with the status of crate, a link to discord, an example in gitpod and documentation in crate Readme.md file.\nFor use this command you need to specify:\n\n[package]\nname = \"test_module\"\nrepository = \"https://github.com/Username/ProjectName/tree/master/module/test_module\"\n...\n[package.metadata]\nstability = \"stable\" (Optional)\ndiscord_url = \"https://discord.gg/1234567890\" (Optional)\n\nin module's Cargo.toml." ) .routine( command::readme_modules_headers_renew ) .end() + + .command( "readme.headers.renew" ) + .hint( "Aggregation of two command : `readme.header.renew` and `readme.modules.headers.renew`.\n Generated headers in workspace members and in main Readme.md file.") + .long_hint( "Generate header which contains a badge with the general status of workspace, a link to discord, an example in gitpod and documentation in workspace`s Readme.md file.\n For use this command you need to specify:\n\n[workspace.metadata]\nmaster_branch = \"alpha\"\nworkspace_name = \"wtools\"\nrepo_url = \"https://github.com/Wandalen/wTools\"\ndiscord_url = \"https://discord.gg/123123\"\n\nin workspace's Cargo.toml.\n\nGenerates header for each workspace member which contains a badge with the status of crate, a link to discord, an example in gitpod and documentation in crate Readme.md file.\nFor use this command you need to specify:\n\n[package]\nname = \"test_module\"\nrepository = \"https://github.com/Username/ProjectName/tree/master/module/test_module\"\n...\n[package.metadata]\nstability = \"stable\" (Optional)\ndiscord_url = \"https://discord.gg/1234567890\" (Optional)\n\nin module's Cargo.toml.") + .routine( command::readme_headers_renew ) + .end() + + .command( "features" ) + .hint( "Lists features of the package" ) + .long_hint( "Lists features of the package located in a folder.\nWill list either separate package features or features for every package of a workspace") + .subject() + .hint( "Provide path to the package that you want to check.\n\t The path should point to a directory that contains a `Cargo.toml` file." ) + .kind( Type::Path ) + .optional( true ) + .end() + .property("with_features_deps") + .hint( "Display dependencies of features of the package" ) + .kind( Type::Bool ) + .optional( true ) + .end() + .routine( command::features ) + .end() } } @@ -270,6 +291,8 @@ crate::mod_interface! layer publish; /// Used to compare local and published versions of a specific package. layer publish_diff; + /// Combination of two commands `main_header` and `readme_modules_headers_renew`. + layer readme_headers_renew; /// Generates health table in main Readme.md file of workspace. // aaa : for Petro : what a table?? // aaa : add more details to documentation @@ -286,5 +309,6 @@ crate::mod_interface! layer main_header; /// Generate headers layer readme_modules_headers_renew; - + /// List features + layer features; } diff --git a/module/move/willbe/src/command/publish.rs b/module/move/willbe/src/command/publish.rs index 2724b33b78..9b1d09eff0 100644 --- a/module/move/willbe/src/command/publish.rs +++ b/module/move/willbe/src/command/publish.rs @@ -9,9 +9,8 @@ mod private use former::Former; use std::fmt::Write; - // qqq: `Former` forces the struct to be public #[ derive( Former ) ] - pub struct PublishProperties + struct PublishProperties { #[ former( default = true ) ] dry : bool, diff --git a/module/move/willbe/src/command/publish_diff.rs b/module/move/willbe/src/command/publish_diff.rs index cbb029393e..961ba818c4 100644 --- a/module/move/willbe/src/command/publish_diff.rs +++ b/module/move/willbe/src/command/publish_diff.rs @@ -8,9 +8,8 @@ mod private use wtools::error::Result; use _path::AbsolutePath; - // qqq: `Former` forces the struct to be public #[ derive( former::Former ) ] - pub struct PublishDiffProperties + struct PublishDiffProperties { keep_archive : Option< PathBuf >, } diff --git a/module/move/willbe/src/command/readme_headers_renew.rs b/module/move/willbe/src/command/readme_headers_renew.rs new file mode 100644 index 0000000000..d8b4edd5c5 --- /dev/null +++ b/module/move/willbe/src/command/readme_headers_renew.rs @@ -0,0 +1,118 @@ +mod private +{ + use crate::*; + use _path::AbsolutePath; + use action; + use wtools::error::anyhow::Error; + use error_tools::{ Result, err }; + use std::fmt::{ Display, Formatter }; + + #[ derive( Debug, Default ) ] + struct ReadmeHeadersRenewReport + { + main_header_renew_report : action::MainHeaderRenewReport, + main_header_renew_error : Option< Error >, + modules_headers_renew_report : action::ModulesHeadersRenewReport, + modules_headers_renew_error : Option< Error >, + } + + impl Display for ReadmeHeadersRenewReport + { + fn fmt( &self, f : &mut Formatter< '_ > ) -> std::fmt::Result + { + match ( &self.main_header_renew_error, &self.modules_headers_renew_error ) + { + ( Some( main ), Some( modules ) ) => + { + writeln! + ( + f, + "Main header renew report : \n{}\nError : \n{:?}\nModules headers renew report : \n{}\nError : \n{:?}", + self.main_header_renew_report, main, self.modules_headers_renew_report, modules + )?; + } + ( Some( main ), None ) => + { + writeln! + ( + f, + "Main header renew report : \n{}\nError : \n{:?}\nModules headers renew report : \n{}", + self.main_header_renew_report, main, self.modules_headers_renew_report + )?; + } + ( None, Some( modules) ) => + { + writeln! + ( + f, + "Main header renew report : \n{}\nModules headers renew report : \n{}\nError : \n{:?}\n", + self.main_header_renew_report, self.modules_headers_renew_report, modules + )?; + } + ( None, None ) => + { + writeln! + ( + f, + "Main header renew report : \n{}\n\nModules headers renew report : \n{}", + self.main_header_renew_report, self.modules_headers_renew_report + )?; + } + } + Ok( () ) + } + } + + + /// Aggregates two commands: `generate_modules_headers` & `generate_main_header` + pub fn readme_headers_renew() -> Result< () > + { + let mut report = ReadmeHeadersRenewReport::default(); + let absolute_path = AbsolutePath::try_from( std::env::current_dir()? )?; + let mut fail = false; + + match action::readme_header_renew( absolute_path.clone() ) + { + Ok( r ) => + { + report.main_header_renew_report = r; + } + Err( ( r, error ) ) => + { + fail = true; + report.main_header_renew_report = r; + report.main_header_renew_error = Some( Error::from( error ) ); + } + }; + match action::readme_modules_headers_renew( absolute_path ) + { + Ok( r ) => + { + report.modules_headers_renew_report = r; + } + Err( ( r, error ) ) => + { + fail = true; + report.modules_headers_renew_report = r; + report.modules_headers_renew_error = Some( Error::from( error ) ); + } + } + + if fail + { + eprintln!( "{report}" ); + Err( err!( "Something went wrong" ) ) + } + else + { + println!( "{report}" ); + Ok( () ) + } + } +} + +crate::mod_interface! +{ + /// Generate header's. + orphan use readme_headers_renew; +} \ No newline at end of file diff --git a/module/move/willbe/src/command/readme_modules_headers_renew.rs b/module/move/willbe/src/command/readme_modules_headers_renew.rs index af1c8c0805..e959c12365 100644 --- a/module/move/willbe/src/command/readme_modules_headers_renew.rs +++ b/module/move/willbe/src/command/readme_modules_headers_renew.rs @@ -2,12 +2,24 @@ mod private { use crate::*; use _path::AbsolutePath; - use wtools::error::{ for_app::Context, Result }; + use wtools::error::{ for_app::Error, Result }; /// Generate headers for workspace members pub fn readme_modules_headers_renew() -> Result< () > { - action::readme_modules_headers_renew( AbsolutePath::try_from( std::env::current_dir()? )? ).context( "Fail to generate headers" ) + match action::readme_modules_headers_renew( AbsolutePath::try_from( std::env::current_dir()? )? ) + { + Ok( report ) => + { + println!( "{report}" ); + Ok( () ) + } + Err( ( report, e ) ) => + { + eprintln!( "{report}" ); + Err( Error::from( e ).context( "Fail to generate modules headers." ) ) + } + } } } diff --git a/module/move/willbe/src/command/test.rs b/module/move/willbe/src/command/test.rs index e97ff16b5f..25019344fb 100644 --- a/module/move/willbe/src/command/test.rs +++ b/module/move/willbe/src/command/test.rs @@ -15,9 +15,8 @@ mod private use error_tools::for_app::bail; use optimization::Optimization; - // qqq: `Former` forces the struct to be public #[ derive( Former, Debug ) ] - pub struct TestsProperties + struct TestsProperties { #[ former( default = true ) ] dry : bool, diff --git a/module/move/willbe/src/command/workspace_renew.rs b/module/move/willbe/src/command/workspace_renew.rs index 4307e20045..26cc520bf4 100644 --- a/module/move/willbe/src/command/workspace_renew.rs +++ b/module/move/willbe/src/command/workspace_renew.rs @@ -7,9 +7,8 @@ mod private use wtools::error::{ anyhow::Context, Result }; use action::WorkspaceTemplate; - // qqq: `Former` forces the struct to be public #[ derive( Former ) ] - pub struct WorkspaceNewProperties + struct WorkspaceNewProperties { repository_url : String, branches : Vec< String >, diff --git a/module/move/willbe/src/entity/features.rs b/module/move/willbe/src/entity/features.rs index 81c1452180..54f38b2d22 100644 --- a/module/move/willbe/src/entity/features.rs +++ b/module/move/willbe/src/entity/features.rs @@ -142,7 +142,7 @@ mod private estimate } - + } crate::mod_interface! diff --git a/module/move/willbe/src/entity/manifest.rs b/module/move/willbe/src/entity/manifest.rs index fdb04d89bb..d167175177 100644 --- a/module/move/willbe/src/entity/manifest.rs +++ b/module/move/willbe/src/entity/manifest.rs @@ -18,9 +18,11 @@ pub( crate ) mod private }; use _path::AbsolutePath; + /// `CrateDirError` enum represents errors when creating a `CrateDir` object. #[ derive( Debug, Error ) ] pub enum CrateDirError { + /// Indicates a validation error with a descriptive message. #[ error( "Failed to create a `CrateDir` object due to `{0}`" ) ] Validation( String ), } @@ -287,6 +289,7 @@ crate::mod_interface! exposed use Manifest; exposed use CrateDir; orphan use ManifestError; + orphan use CrateDirError; protected use open; protected use repo_url; } diff --git a/module/move/willbe/src/entity/optimization.rs b/module/move/willbe/src/entity/optimization.rs index c6163fd794..a2c1a58241 100644 --- a/module/move/willbe/src/entity/optimization.rs +++ b/module/move/willbe/src/entity/optimization.rs @@ -13,6 +13,7 @@ mod private Release, } + // qqq : use derive impl std::fmt::Display for Optimization { fn fmt( &self, f : &mut Formatter< '_ > ) -> std::fmt::Result @@ -30,4 +31,4 @@ mod private crate::mod_interface! { protected use Optimization; -} \ No newline at end of file +} diff --git a/module/move/willbe/src/entity/package.rs b/module/move/willbe/src/entity/package.rs index ce0e6575fe..9e1f281ebc 100644 --- a/module/move/willbe/src/entity/package.rs +++ b/module/move/willbe/src/entity/package.rs @@ -317,7 +317,7 @@ mod private } #[ derive( Debug, Clone ) ] - pub struct GitThingsOptions + pub struct GitOptions { pub git_root : AbsolutePath, pub items : Vec< AbsolutePath >, @@ -325,7 +325,7 @@ mod private pub dry : bool, } - fn perform_git_commit( o : GitThingsOptions ) -> Result< ExtendedGitReport > + fn perform_git_commit( o : GitOptions ) -> Result< ExtendedGitReport > { let mut report = ExtendedGitReport::default(); if o.items.is_empty() { return Ok( report ); } @@ -352,8 +352,7 @@ mod private pub package_name : String, pub pack : cargo::PackOptions, pub version_bump : version::BumpOptions, - // qqq : rename - pub git_things : GitThingsOptions, + pub git_options : GitOptions, pub publish : cargo::PublishOptions, pub dry : bool, } @@ -396,7 +395,7 @@ mod private dependencies : dependencies.clone(), dry : self.dry, }; - let git_things = GitThingsOptions + let git_options = GitOptions { git_root : workspace_root, items : dependencies.iter().chain([ &crate_dir ]).map( | d | d.absolute_path().join( "Cargo.toml" ) ).collect(), @@ -407,6 +406,7 @@ mod private { path : crate_dir.as_ref().into(), temp_path : self.base_temp_dir.clone(), + retry_count : 2, dry : self.dry, }; @@ -415,7 +415,7 @@ mod private package_name : self.package.name().unwrap(), pack, version_bump, - git_things, + git_options, publish, dry : self.dry, } @@ -439,13 +439,13 @@ mod private package_name: _, mut pack, mut version_bump, - mut git_things, + mut git_options, mut publish, dry, } = instruction; pack.dry = dry; version_bump.dry = dry; - git_things.dry = dry; + git_options.dry = dry; publish.dry = dry; report.get_info = Some( cargo::pack( pack ).map_err( | e | ( report.clone(), e ) )? ); @@ -453,8 +453,8 @@ mod private report.publish_required = true; let bump_report = version::version_bump( version_bump ).map_err( | e | ( report.clone(), e ) )?; report.bump = Some( bump_report.clone() ); - let git_root = git_things.git_root.clone(); - let git = match perform_git_commit( git_things ) + let git_root = git_options.git_root.clone(); + let git = match perform_git_commit( git_options ) { Ok( git ) => git, Err( e ) => @@ -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. - #[ scalar( setter = false, hint = false ) ] + #[ scalar( setter = false ) ] pub plans : Vec< PackagePublishInstruction >, } @@ -612,7 +612,7 @@ mod private self.storage.base_temp_dir = path; self } - + pub fn package< IntoPackage >( mut self, package : IntoPackage ) -> Self where IntoPackage : Into< Package >, diff --git a/module/move/willbe/src/entity/test.rs b/module/move/willbe/src/entity/test.rs index 7735bc5af1..b8298d7f4e 100644 --- a/module/move/willbe/src/entity/test.rs +++ b/module/move/willbe/src/entity/test.rs @@ -14,7 +14,7 @@ mod private }; use std::collections::HashMap; use std::ffi::OsString; - use std::fmt::{ Debug, Display }; + use std::fmt::{ Debug, Display }; /* qqq : import only fmt here and everywhere */ use std::marker::PhantomData; use std::path::PathBuf; // aaa : for Petro : don't use cargo_metadata directly, use facade @@ -24,10 +24,10 @@ mod private // qqq : for Petro : don't do micro imports #[ cfg( feature = "progress_bar" ) ] use indicatif:: - { - MultiProgress, - ProgressBar, - ProgressStyle + { + MultiProgress, + ProgressBar, + ProgressStyle }; use rayon::ThreadPoolBuilder; use process_tools::process::*; @@ -173,11 +173,11 @@ mod private let mut table = Table::default(); // let format = format(); // table.set_format( format ); - + let mut header_row = Row::new(); header_row.add_cell( "Channel" ); header_row.add_cell( "Opt" ); - + for feature in &ff { header_row.add_cell( feature ); @@ -273,7 +273,7 @@ mod private } } - fn generate_features_cells( ff : &mut Vec< String >, variant : &TestVariant, row : &mut Row, mut counter : usize, mut flag : bool, enabled_features : &BTreeSet< String > ) + fn generate_features_cells( ff : &mut Vec< String >, variant : &TestVariant, row : &mut Row, mut counter : usize, mut flag : bool, enabled_features : &BTreeSet< String > ) { for feature in ff { @@ -282,12 +282,12 @@ mod private { flag = false; row.add_cell( c ); - } + } else if variant.features.contains( feature ) { row.add_cell( c ); - } - else + } + else { c = ""; row.add_cell( c ); @@ -295,7 +295,7 @@ mod private counter += 1; } } - + #[ derive( Debug, Former ) ] pub struct PackageTestOptions< 'a > { @@ -440,7 +440,7 @@ mod private pub feature : Option< TestOptionsProgressBarFeature >, } - // qqq : remove after Former fix + // qqq : for Petro : remove after Former fix /// Structure for progress bar feature field pub struct TestOptionsProgressBarFeature { @@ -549,7 +549,7 @@ mod private header_row.add_cell( feature ); } table.set_header( header_row ); - + writeln!( f, "{} {}\n", "\n=== Module".bold(), self.package_name.0.bold() )?; if self.tests.is_empty() { @@ -709,11 +709,11 @@ mod private let s = if let Some( multi_progress ) = options.progress_bar_feature.as_ref().and_then( | f | f.multi_progress.as_ref() ) { let s = multi_progress.add( ProgressBar::new_spinner().with_message( format!( "{}", variant ) ) ); - s.enable_steady_tick( std::time::Duration::from_millis( 100 ) ); + s.enable_steady_tick( std::time::Duration::from_millis( 100 ) ); Some( s ) } - else - { + else + { None }; // spinner.enable_steady_tick( std::time::Duration::from_millis( 100 ) ); @@ -772,8 +772,8 @@ mod private pb.inc( 0 ); Some( pb ) } - else - { + else + { None }; pb diff --git a/module/move/willbe/src/entity/version.rs b/module/move/willbe/src/entity/version.rs index d20329839e..e3900e1d57 100644 --- a/module/move/willbe/src/entity/version.rs +++ b/module/move/willbe/src/entity/version.rs @@ -68,13 +68,8 @@ mod private pub fn bump( self ) -> Self { let mut ver = self.0; - if ver.major != 0 - { - ver.major += 1; - ver.minor = 0; - ver.patch = 0; - } - else if ver.minor != 0 + // we shouldn't change the major part of a version yet + if ver.minor != 0 || ver.major != 0 { ver.minor += 1; ver.patch = 0; @@ -170,6 +165,7 @@ mod private } // qqq : we have to replace the implementation above with the implementation below, don't we? + // qqq : for Bohdan : duplication? /// `BumpOptions` manages the details necessary for the version bump process for crates. /// This includes the directory of the crate whose version is being bumped, the old and new version numbers, @@ -300,7 +296,7 @@ mod private Ok( report ) } - + /// Reverts the version of a package in the provided `ExtendedBumpReport`. /// /// # Arguments @@ -315,7 +311,7 @@ mod private let Some( name ) = report.name.as_ref() else { return Ok( () ) }; let Some( old_version ) = report.old_version.as_ref() else { return Ok( () ) }; let Some( new_version ) = report.new_version.as_ref() else { return Ok( () ) }; - + let dependencies = | item_maybe_with_dependencies : &mut toml_edit::Item | { if let Some( dependency ) = item_maybe_with_dependencies.get_mut( "dependencies" ).and_then( | ds | ds.get_mut( name ) ) @@ -335,10 +331,10 @@ mod private } } } - + Ok( () ) }; - + for path in &report.changed_files { let mut manifest = manifest::open( path.clone() )?; diff --git a/module/move/willbe/src/entity/workspace.rs b/module/move/willbe/src/entity/workspace.rs index 26a586e6d9..b477aa3c97 100644 --- a/module/move/willbe/src/entity/workspace.rs +++ b/module/move/willbe/src/entity/workspace.rs @@ -108,7 +108,6 @@ mod private { &self.inner.features } - } /// A dependency of the main crate diff --git a/module/move/willbe/src/tool/_path.rs b/module/move/willbe/src/tool/_path.rs index cd094111f8..a8209366f7 100644 --- a/module/move/willbe/src/tool/_path.rs +++ b/module/move/willbe/src/tool/_path.rs @@ -101,7 +101,7 @@ pub( crate ) mod private /// Check if path has a glob. #[ allow( dead_code ) ] - pub fn glob_is( path : &str ) -> bool + fn glob_is( path : &str ) -> bool { let glob_chars = "*?[{"; let mut last_char = ' '; @@ -159,7 +159,8 @@ pub( crate ) mod private crate::mod_interface! { - protected use glob_is; + // qqq : remove this? + // protected use glob_is; protected use canonicalize; protected use unique_folder_name; diff --git a/module/move/willbe/src/tool/cargo.rs b/module/move/willbe/src/tool/cargo.rs index 259c828791..d24ac23565 100644 --- a/module/move/willbe/src/tool/cargo.rs +++ b/module/move/willbe/src/tool/cargo.rs @@ -5,6 +5,7 @@ mod private use std::path::PathBuf; use error_tools::err; + use error_tools::for_app::format_err; use former::Former; use process_tools::process::*; use wtools::error::Result; @@ -92,6 +93,8 @@ mod private { pub( crate ) path : PathBuf, pub( crate ) temp_path : Option< PathBuf >, + #[ former( default = 0usize ) ] + pub( crate ) retry_count : usize, pub( crate ) dry : bool, } @@ -140,11 +143,29 @@ mod private } else { - Run::former() - .bin_path( program ) - .args( arguments.into_iter().map( OsString::from ).collect::< Vec< _ > >() ) - .current_path( args.path ) - .run().map_err( | report | err!( report.to_string() ) ) + let mut results = Vec::with_capacity( args.retry_count + 1 ); + let run_args = arguments.into_iter().map( OsString::from ).collect::< Vec< _ > >(); + for _ in 0 .. args.retry_count + 1 + { + let result = Run::former() + .bin_path( program ) + .args( run_args.clone() ) + .current_path( &args.path ) + .run(); + match result + { + Ok( report ) => return Ok( report ), + Err( e ) => results.push( e ), + } + } + if args.retry_count > 0 + { + Err( format_err!( "It took {} attempts, but still failed. Here are the errors:\n{}", args.retry_count + 1, results.into_iter().map( | r | format!( "- {r}" ) ).collect::< Vec< _ > >().join( "\n" ) ) ) + } + else + { + Err( results.remove( 0 ) ).map_err( | report | err!( report.to_string() ) ) + } } } } diff --git a/module/move/willbe/src/tool/template.rs b/module/move/willbe/src/tool/template.rs index 044f1157b9..ae07683d4b 100644 --- a/module/move/willbe/src/tool/template.rs +++ b/module/move/willbe/src/tool/template.rs @@ -107,7 +107,7 @@ mod private #[ derive( Debug, Default, Former ) ] pub struct TemplateParameters { - #[ subform( setter = false ) ] + #[ subform_entry( setter = false ) ] descriptors : Vec< TemplateParameterDescriptor > } @@ -147,7 +147,7 @@ mod private pub fn parameter( self, name : &str ) -> TemplateParameterDescriptorAsSubformer< Self, impl TemplateParameterDescriptorAsSubformerEnd< Self > > { - self._descriptors_add::< TemplateParameterDescriptorFormer< _ >, _ >() + self._descriptors_subform_entry::< TemplateParameterDescriptorFormer< _ >, _ >() .parameter( name ) } } @@ -302,8 +302,8 @@ mod private pub struct TemplateFilesBuilder { /// Stores all file descriptors for current template. - #[ subform( setter = true ) ] - #[ scalar( setter = false, hint = false ) ] + #[ subform_entry( setter = true ) ] + #[ scalar( setter = false ) ] pub files : Vec< TemplateFileDescriptor >, } @@ -314,7 +314,7 @@ mod private #[ inline( always ) ] pub fn file( self ) -> TemplateFileDescriptorAsSubformer< Self, impl TemplateFileDescriptorAsSubformerEnd< Self > > { - self._files_add() + self._files_subform_entry() } } diff --git a/module/move/willbe/template/workflow/Readme.md b/module/move/willbe/template/workflow/Readme.md new file mode 100644 index 0000000000..52ce58143f --- /dev/null +++ b/module/move/willbe/template/workflow/Readme.md @@ -0,0 +1,133 @@ +# healthtable + +[healthtable](../../Readme.md) - in addition to information about modules, their stability contains the results of CI/CD of the master and alpha branches. + +# for_pr_rust_push.yml + +actions: +- install stable rust +- install nightly rust +- install willbe +- run tests with all features, but only on stable toolchain and in debug optimization mode + +Groups creates by strategy: +```yml +for_pr_rust_push_${{ inputs.module_name }}_${{ github.ref }}_ + ${{ contains( inputs.commit_message, '+test' ) || startsWith( inputs.commit_message, 'merge' ) }} +``` + +inputs.module_name - name of module +github.ref - name of branch +{{ contains( inputs.commit_message, '+test' ) || startsWith( inputs.commit_message, 'merge' ) }} - returns true if commit message contains directive `+test` or starts with `merge` word. + +runs if commit message contains directive `+test` or starts with `merge` word. + +# standard_rust_push.yml + +actions: +- install stable rust +- install nightly rust +- install cargo-udeps +- install cargo-audit +- checks crate with cargo-udeps +- checks crete with cargo-audit +- install willbe +- run tests with all features, with stable and nightly toolchain, with release and debug optimization mode + +Groups creates by strategy: +```yml +standard_rust_push_${{ inputs.module_name }}_${{ github.ref }}_ + ${{ contains( inputs.commit_message, '+test' ) || startsWith( inputs.commit_message, 'merge' ) }} +``` + +inputs.module_name - name of module +github.ref - name of branch +{{ contains( inputs.commit_message, '+test' ) || startsWith( inputs.commit_message, 'merge' ) }} - returns true if commit message contains directive `+test` or starts with `merge` word. + +runs if commit message contains directive `+test` or starts with `merge` word. + +# standard_rust_pool_request.yml + +actions: +- call [for_pr_rust_push.yml](#for_pr_rust_pushyml) for all project. + +# standard_rust_schedule.yml + +actions: +- call [standard_rust_push.yml](#standard_rust_pushyml) for `{module_name}` every day at 1:00 a.m. + +Affects badges in the header of the workspace readme.md file, looks like this [![wTools](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/standard_rust_scheduled.yml?label=master&logo=github&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/standard_rust_scheduled.yml). + +# module_{module_name}_push.yml + +actions: +- call [standard_rust_push.yml](#standard_rust_pushyml) for `{module_name}`. + +Affects badges that are opposite to modules in the **[healthtable](#healthtable)**, as well as badges in the header of the crate readme.md files. + + +# appropriate_branch.yml + +This workflow ensures that pull requests are opened against the correct target branches based on a predefined branching strategy (alpha -> beta -> master). It checks whether the destination branch specified in the pull request matches the expected branch according to the branching strategy. If it doesn't match, the pull request is converted to draft mode, and if it still doesn't match, the workflow fails. + +# appropriate_branch_beta.yml + +This workflow delegates the actual validation and actions to another workflow file (appropriate_branch.yml) located in the Wandalen/wTools repository under .github/workflows directory on the "alpha" branch. It ensures that pull requests targeting the "beta" branch are appropriately validated and processed according to the rules defined in the external workflow file. + +# appropriate_branch_master.yml + +Similar to the previous workflow, this one also delegates the validation and processing of pull requests to an external workflow file (appropriate_branch.yml) located in the Wandalen/wTools repository under the .github/workflows directory on the "alpha" branch. +By specifying the "beta" branch as the source branch and dynamically referencing the base branch of the pull request as the destination branch, this workflow ensures that pull requests targeting the "main" or "master" branches are appropriately validated and processed according to the rules defined in the external workflow file. +This setup promotes consistency and reusability of workflow logic across different branches within the repository, helping to maintain a standardized process for handling pull requests. + +# auto_merge_to_beta.yml + +This workflow automates the process of merging changes from the "alpha" branch into the "beta" branch after ensuring that related workflow runs for modules have completed successfully. +It waits for the completion of workflow runs related to modules and checks their statuses before proceeding with the merge process. +If all checks pass, it merges the changes into the "beta" branch using the provided GitHub token. + +# auto_pr.yml + +This workflow automates the process of opening pull requests between specified source and destination branches. +Upon triggering, it checks out the repository and opens a pull request from the source branch (src_branch) to the destination branch (dst_branch). +The pull request title is automatically generated to indicate that it's an automated pull request forwarding from one branch to another. +If a pull request already exists between the specified branches and PASS_IF_EXISTS is set to true, the action will pass without creating a new pull request. + +# auto_pr_to_alpha.yml + +This workflow automates the process of opening pull requests from any branch except for those explicitly excluded to the "alpha" branch. +It leverages branch filtering to include all branches and exclude specific ones such as master, main, alpha, beta, and any branches containing test or experiment in their names. +When triggered by a push event on a qualifying branch, it calls the external workflow (auto_pr.yml) to handle the process of opening a pull request to the "alpha" branch, passing the source and destination branch information along with the GitHub bot token for authentication. + +# auto_pr_to_beta.yml + +This workflow automates the process of opening pull requests from the "alpha" branch to the "beta" branch. +When triggered by a push event on the "alpha" branch, it calls the external workflow (auto_pr.yml) to handle the process of opening a pull request to the "beta" branch, passing the source and destination branch information along with the GitHub bot token for authentication. + +# auto_pr_to_master.yml + +This workflow automates the process of opening pull requests from the "beta" branch to the "master" branch. +When triggered by a push event on the "beta" branch, it calls the external workflow (auto_pr.yml) to handle the process of opening a pull request to the "master" branch, passing the source and destination branch information along with the GitHub bot token for authentication. + +# runs_clean.yml + +This workflow allows manual triggering to clean up workflow runs in the repository. +It first deletes any runs that have been cancelled or skipped, ensuring that they do not clutter the workflow history. +Then, it deletes runs older than a specified number of days, while ensuring that at least 20 runs are preserved regardless of their age. +By regularly cleaning up older workflow runs, this workflow helps maintain a clean and organized workflow history in the repository. + +# standard_rust_status.yml + +This workflow serves as a status monitor for the completion of specific workflows: "auto_merge_to_beta" and "rust_scheduled." +Upon completion of any of these workflows, it checks the status of their runs. +It employs a matrix strategy to iterate over different workflow files to check their statuses. +If the conclusion of any checked workflow run is "failure," "cancelled," or "skipped," the workflow exits with an error, indicating a problem. + +# status_checks_rules_update.yml + +When a pull request is opened targeting branches "alpha" or "beta": +- If the base branch is "beta": + - It compares the contents of the workflow directories between branches "alpha" and "beta". + - If they are not equal, it triggers an update of branch protection rules for the "beta" branch. +- If the base branch is "alpha": + - It directly triggers an update of branch protection rules for the "alpha" branch with specific required status checks for different contexts. \ No newline at end of file diff --git a/module/move/willbe/template/workflow/for_pr_rust_push.yml b/module/move/willbe/template/workflow/for_pr_rust_push.yml new file mode 100644 index 0000000000..edbc5aa907 --- /dev/null +++ b/module/move/willbe/template/workflow/for_pr_rust_push.yml @@ -0,0 +1,72 @@ + +name : for_pr_push + +on : + + workflow_call : + inputs : + manifest_path : + required : true + type : string + module_name : + required : true + type : string + commit_message : + required : true + type : string + with_smoke : + required : false + type : string + default : true + +concurrency : + + group : for_pr_rust_push_${{ inputs.module_name }}_${{ github.ref }}_ + ${{ contains( inputs.commit_message, '+test' ) || startsWith( inputs.commit_message, 'merge' ) }} + cancel-in-progress : true + +env : + + RUST_BACKTRACE : 1 + CARGO_TERM_COLOR : always + WITH_SMOKE : ${{ inputs.with_smoke }} + +jobs : + + will_test : + if : contains( inputs.commit_message, '+test' ) || contains( inputs.commit_message, 'merge' ) + concurrency : + group : for_pr_rust_push_${{ inputs.module_name }}_${{ github.ref }}_${{ matrix.os }} + cancel-in-progress : true + strategy : + fail-fast : false + matrix : + os : [ ubuntu-latest, windows-latest, macos-latest ] + runs-on : ${{ matrix.os }} + steps : + - name : Install latest stable toolchain + uses : Wandalen/wretry.action/main@master + with : + action : actions-rs/toolchain@v1 + with : | + toolchain : stable + override : true + attempt_limit : 3 + attempt_delay: 10000 + - name: Install latest nightly toolchain + uses: Wandalen/wretry.action/main@master + with: + action: actions-rs/toolchain@v1 + with: | + toolchain : nightly + override : true + attempt_limit: 3 + attempt_delay: 10000 + - uses: actions/checkout@v3 + - name: Install will + run: cargo install --git https://github.com/Wandalen/wTools --branch alpha willbe + - name: Set MANIFEST_ROOT_PATH + id: rootpath + run: echo "::set-output name=path::$(dirname ${{ inputs.manifest_path }})" + - name: Run tests with each feature + run: will .test ${{ steps.rootpath.outputs.path }}/ dry:0 exclude:'' with_all_features:1 with_debug:1 with_nightly:0 with_none_features:1 with_release:0 with_stable:1 \ No newline at end of file diff --git a/module/move/willbe/template/workflow/standard_rust_pull_request.hbs b/module/move/willbe/template/workflow/standard_rust_pull_request.hbs index 844d1e2578..dc2999f144 100644 --- a/module/move/willbe/template/workflow/standard_rust_pull_request.hbs +++ b/module/move/willbe/template/workflow/standard_rust_pull_request.hbs @@ -43,8 +43,9 @@ jobs : tested : needs: check if : $\{{ needs.check.outputs.should_run == 'true' }} - uses : {{username_and_repository}}/.github/workflows/standard_rust_push.yml@alpha + uses : {{username_and_repository}}/.github/workflows/for_pr_rust_push.yml@alpha with : manifest_path : './Cargo.toml' module_name : $\{{ github.event.base.ref }}_$\{{ github.event.number }} commit_message : "+test_$\{{ github.event.base.ref }}_$\{{ github.event.number }}" + with_smoke : false diff --git a/module/move/willbe/template/workflow/standard_rust_push.yml b/module/move/willbe/template/workflow/standard_rust_push.yml index 5e95425a98..1812c69512 100644 --- a/module/move/willbe/template/workflow/standard_rust_push.yml +++ b/module/move/willbe/template/workflow/standard_rust_push.yml @@ -22,8 +22,7 @@ on : concurrency : group : standard_rust_push_${{ inputs.module_name }}_${{ github.ref }}_ - ${{ contains( inputs.commit_message, '+test' ) || startsWith( inputs.commit_message, 'Merge' ) || contains( inputs.commit_message, inputs.module_name ) }}_ - ${{ !contains( inputs.commit_message, '!only_js' )}} + ${{ contains( inputs.commit_message, '+test' ) || startsWith( inputs.commit_message, 'merge' ) }} cancel-in-progress : true env : diff --git a/module/move/willbe/tests/asset/three_packages_with_features/Cargo.toml b/module/move/willbe/tests/asset/three_packages_with_features/Cargo.toml new file mode 100644 index 0000000000..49f36c395b --- /dev/null +++ b/module/move/willbe/tests/asset/three_packages_with_features/Cargo.toml @@ -0,0 +1,8 @@ +[workspace] +resolver = "2" +members = [ + "*", +] + +[workspace.metadata] +discord_url = "https://discord.gg/123456789" diff --git a/module/move/willbe/tests/asset/three_packages_with_features/b/Cargo.toml b/module/move/willbe/tests/asset/three_packages_with_features/b/Cargo.toml new file mode 100644 index 0000000000..b9c97a9443 --- /dev/null +++ b/module/move/willbe/tests/asset/three_packages_with_features/b/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "_chain_of_packages_b" +version = "0.1.0" +edition = "2021" +repository = "https://github.com/Username/test/b" + +[package.metadata] +stability = "stable" +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +_chain_of_packages_c = { path = "../c", optional = true } + +[features] +enabled = [] +default = ["boo"] +boo = ["_chain_of_packages_c"] diff --git a/module/move/willbe/tests/asset/three_packages_with_features/b/Readme.md b/module/move/willbe/tests/asset/three_packages_with_features/b/Readme.md new file mode 100644 index 0000000000..8c938fa512 --- /dev/null +++ b/module/move/willbe/tests/asset/three_packages_with_features/b/Readme.md @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/module/move/willbe/tests/asset/three_packages_with_features/b/src/lib.rs b/module/move/willbe/tests/asset/three_packages_with_features/b/src/lib.rs new file mode 100644 index 0000000000..e9b1860dae --- /dev/null +++ b/module/move/willbe/tests/asset/three_packages_with_features/b/src/lib.rs @@ -0,0 +1,17 @@ +pub fn add( left : usize, right : usize ) -> usize +{ + left + right +} + +#[ cfg( test ) ] +mod tests +{ + use super::*; + + #[ test ] + fn it_works() + { + let result = add( 2, 2 ); + assert_eq!( result, 4 ); + } +} diff --git a/module/move/willbe/tests/asset/three_packages_with_features/c/Cargo.toml b/module/move/willbe/tests/asset/three_packages_with_features/c/Cargo.toml new file mode 100644 index 0000000000..0bcd46b4e3 --- /dev/null +++ b/module/move/willbe/tests/asset/three_packages_with_features/c/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "_chain_of_packages_c" +version = "0.1.0" +edition = "2021" +repository = "https://github.com/Username/test/c" + +[package.metadata] +discord_url = "https://discord.gg/m3YfbXpUUY" +stability = "stable" +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] + +[features] +enabled = [] +default = ["foo"] +foo = [] \ No newline at end of file diff --git a/module/move/willbe/tests/asset/three_packages_with_features/c/Readme.md b/module/move/willbe/tests/asset/three_packages_with_features/c/Readme.md new file mode 100644 index 0000000000..8c938fa512 --- /dev/null +++ b/module/move/willbe/tests/asset/three_packages_with_features/c/Readme.md @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/module/move/willbe/tests/asset/three_packages_with_features/c/src/lib.rs b/module/move/willbe/tests/asset/three_packages_with_features/c/src/lib.rs new file mode 100644 index 0000000000..e9b1860dae --- /dev/null +++ b/module/move/willbe/tests/asset/three_packages_with_features/c/src/lib.rs @@ -0,0 +1,17 @@ +pub fn add( left : usize, right : usize ) -> usize +{ + left + right +} + +#[ cfg( test ) ] +mod tests +{ + use super::*; + + #[ test ] + fn it_works() + { + let result = add( 2, 2 ); + assert_eq!( result, 4 ); + } +} diff --git a/module/move/willbe/tests/asset/three_packages_with_features/d/Cargo.toml b/module/move/willbe/tests/asset/three_packages_with_features/d/Cargo.toml new file mode 100644 index 0000000000..a6e5f08b8f --- /dev/null +++ b/module/move/willbe/tests/asset/three_packages_with_features/d/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "_chain_of_packages_d" +version = "0.1.0" +edition = "2021" +repository = "https://github.com/Username/test/c" + +[package.metadata] +stability = "stable" +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] + +[features] +enabled = [] diff --git a/module/move/willbe/tests/asset/three_packages_with_features/d/Readme.md b/module/move/willbe/tests/asset/three_packages_with_features/d/Readme.md new file mode 100644 index 0000000000..8c938fa512 --- /dev/null +++ b/module/move/willbe/tests/asset/three_packages_with_features/d/Readme.md @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/module/move/willbe/tests/asset/three_packages_with_features/d/src/lib.rs b/module/move/willbe/tests/asset/three_packages_with_features/d/src/lib.rs new file mode 100644 index 0000000000..e9b1860dae --- /dev/null +++ b/module/move/willbe/tests/asset/three_packages_with_features/d/src/lib.rs @@ -0,0 +1,17 @@ +pub fn add( left : usize, right : usize ) -> usize +{ + left + right +} + +#[ cfg( test ) ] +mod tests +{ + use super::*; + + #[ test ] + fn it_works() + { + let result = add( 2, 2 ); + assert_eq!( result, 4 ); + } +} diff --git a/module/move/willbe/tests/inc/action/cicd_renew.rs b/module/move/willbe/tests/inc/action/cicd_renew.rs index a2da8fec84..4f9b0bc17b 100644 --- a/module/move/willbe/tests/inc/action/cicd_renew.rs +++ b/module/move/willbe/tests/inc/action/cicd_renew.rs @@ -109,9 +109,11 @@ fn default_case() assert!( base_path.join( "runs_clean.yml" ).exists() ); assert!( base_path.join( "standard_rust_pull_request.yml" ).exists() ); assert!( base_path.join( "standard_rust_push.yml" ).exists() ); + assert!( base_path.join( "for_pr_rust_push.yml" ).exists() ); assert!( base_path.join( "standard_rust_scheduled.yml" ).exists() ); assert!( base_path.join( "standard_rust_status.yml" ).exists() ); assert!( base_path.join( "status_checks_rules_update.yml" ).exists() ); + assert!( base_path.join( "Readme.md" ).exists() ); } // aaa : for Petro : fix styles diff --git a/module/move/willbe/tests/inc/action/features.rs b/module/move/willbe/tests/inc/action/features.rs new file mode 100644 index 0000000000..c27e99c3b4 --- /dev/null +++ b/module/move/willbe/tests/inc/action/features.rs @@ -0,0 +1,183 @@ +use super::*; +use assert_fs::prelude::*; + +fn arrange( source : &str ) -> assert_fs::TempDir +{ + let root_path = std::path::Path::new( env!( "CARGO_MANIFEST_DIR" ) ); + let assets_relative_path = std::path::Path::new( ASSET_PATH ); + let assets_path = root_path.join( assets_relative_path ); + + let temp = assert_fs::TempDir::new().unwrap(); + temp.copy_from( assets_path.join( source ), &[ "**" ] ).unwrap(); + + temp +} + +#[ test ] +fn package_no_features() +{ + // Arrange + let temp = arrange( "three_packages/b" ); + let options = willbe::action::features::FeaturesOptions::former() + .manifest_dir( willbe::_path::AbsolutePath::try_from( temp.path().to_owned() ).unwrap() ) + .form(); + + // Act + let report = willbe::action::features( options ).unwrap().to_string(); + + // Assert + assert!( report.contains( +"\ +Package _chain_of_packages_b:\ +" ) ); +} + +#[ test ] +fn package_features() +{ + // Arrange + let temp = arrange( "three_packages_with_features/b" ); + let options = willbe::action::features::FeaturesOptions::former() + .manifest_dir( willbe::_path::AbsolutePath::try_from( temp.path().to_owned() ).unwrap() ) + .form(); + + // Act + let report = willbe::action::features( options ).unwrap().to_string(); + + // Assert + assert!( report.contains( +"\ +Package _chain_of_packages_b: +\t_chain_of_packages_c +\tboo +\tdefault +\tenabled\ +" ) ); +} + +#[ test ] +fn package_features_with_features_deps() +{ + let temp = arrange( "three_packages_with_features/b" ); + let options = willbe::action::features::FeaturesOptions::former() + .manifest_dir( willbe::_path::AbsolutePath::try_from( temp.path().to_owned() ).unwrap() ) + .with_features_deps( true ) + .form(); + + // Act + let report = willbe::action::features( options ).unwrap().to_string(); + + // Assert + assert!( report.contains( +"\ +Package _chain_of_packages_b: +\t_chain_of_packages_c: [dep:_chain_of_packages_c] +\tboo: [_chain_of_packages_c] +\tdefault: [boo] +\tenabled: []\ +" ) ); +} + +#[ test ] +fn workspace_no_features() +{ + // Arrange + let temp = arrange( "three_packages" ); + let options = willbe::action::features::FeaturesOptions::former() + .manifest_dir( willbe::_path::AbsolutePath::try_from( temp.path().to_owned() ).unwrap() ) + .form(); + + // Act + let report = willbe::action::features( options ).unwrap().to_string(); + + // Assert + assert!( report.contains( +"\ +Package _chain_of_packages_b:\ +" ) ); + + assert!( report.contains( +"\ +Package _chain_of_packages_c:\ +" ) ); + + assert!( report.contains( +"\ +Package _chain_of_packages_d:\ +" ) ); +} + +#[ test ] +fn workspace_features() +{ + // Arrange + let temp = arrange( "three_packages_with_features" ); + let options = willbe::action::features::FeaturesOptions::former() + .manifest_dir( willbe::_path::AbsolutePath::try_from( temp.path().to_owned() ).unwrap() ) + .form(); + + // Act + let report = willbe::action::features( options ).unwrap().to_string(); + + // Assert + assert!( report.contains( +"\ +Package _chain_of_packages_b: +\t_chain_of_packages_c +\tboo +\tdefault +\tenabled\ +" ) ); + + assert!( report.contains( +"\ +Package _chain_of_packages_c: +\tdefault +\tenabled +\tfoo\ +" ) ); + + assert!( report.contains( +"\ +Package _chain_of_packages_d: +\tenabled\ +" ) ); +} + +#[ test ] +fn workspace_features_with_features_deps() +{ + // Arrange + let temp = arrange( "three_packages_with_features" ); + let options = willbe::action::features::FeaturesOptions::former() + .manifest_dir( willbe::_path::AbsolutePath::try_from( temp.path().to_owned() ).unwrap() ) + .with_features_deps( true ) + .form(); + + // Act + let report = willbe::action::features( options ).unwrap().to_string(); + + // Assert + assert!( report.contains( +"\ +Package _chain_of_packages_b: +\t_chain_of_packages_c: [dep:_chain_of_packages_c] +\tboo: [_chain_of_packages_c] +\tdefault: [boo] +\tenabled: []\ +" ) ); + + assert!( report.contains( +"\ +Package _chain_of_packages_c: +\tdefault: [foo] +\tenabled: [] +\tfoo: []\ +" ) ); + + assert!( report.contains( +"\ +Package _chain_of_packages_d: +\tenabled: []\ +" ) ); +} diff --git a/module/move/willbe/tests/inc/action/mod.rs b/module/move/willbe/tests/inc/action/mod.rs index 66c800260f..ae10e6d259 100644 --- a/module/move/willbe/tests/inc/action/mod.rs +++ b/module/move/willbe/tests/inc/action/mod.rs @@ -1,5 +1,6 @@ use super::*; +pub mod features; pub mod list; pub mod readme_health_table_renew; pub mod readme_modules_headers_renew; @@ -7,5 +8,5 @@ pub mod test; pub mod cicd_renew; pub mod workspace_renew; -// qqq : for Petro : sort +// aaa : for Petro : sort // aaa : sorted & renamed \ No newline at end of file diff --git a/module/move/willbe/tests/inc/entity/features.rs b/module/move/willbe/tests/inc/entity/features.rs index dd4db5bff8..0eaf8e7c75 100644 --- a/module/move/willbe/tests/inc/entity/features.rs +++ b/module/move/willbe/tests/inc/entity/features.rs @@ -265,4 +265,4 @@ fn estimate() assert_eq!( estimate_with( 5, 2, false, true, &[], 0 ), 17 ); assert_eq!( estimate_with( 5, 2, false, false, &[ "feature1".to_string(), "feature2".to_string() ], 2 ), 20 ); assert_eq!( estimate_with( 5, 2, true, true, &[ "feature1".to_string(), "feature2".to_string() ], 2 ), 22 ); -} \ No newline at end of file +} diff --git a/module/move/willbe/tests/inc/entity/version.rs b/module/move/willbe/tests/inc/entity/version.rs index 328bd07834..6ae36602ce 100644 --- a/module/move/willbe/tests/inc/entity/version.rs +++ b/module/move/willbe/tests/inc/entity/version.rs @@ -71,7 +71,7 @@ fn major_without_patches() let new_version = version.bump(); // Assert - assert_eq!( "2.0.0", &new_version.to_string() ); + assert_eq!( "1.1.0", &new_version.to_string() ); } #[ test ] @@ -84,7 +84,7 @@ fn major_with_minor() let new_version = version.bump(); // Assert - assert_eq!( "2.0.0", &new_version.to_string() ); + assert_eq!( "1.2.0", &new_version.to_string() ); } #[ test ] @@ -97,7 +97,7 @@ fn major_with_patches() let new_version = version.bump(); // Assert - assert_eq!( "2.0.0", &new_version.to_string() ); + assert_eq!( "1.2.0", &new_version.to_string() ); } #[ test ]