├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── feature_request.md │ ├── question.md │ └── refactor.md ├── pre-commit ├── pre-push └── workflows │ ├── go-fmt.yml │ ├── go.yml │ ├── golangci-lint.yml │ └── stale.yml ├── .gitignore ├── LICENSE ├── Makefile ├── README-zh_CN.md ├── README.md ├── cmd └── optioner │ └── main.go ├── example ├── examples.go ├── third_party │ └── types.go └── user_option.go ├── go.mod ├── go.sum ├── options └── options_generator.go ├── script ├── fmt.sh └── setup.sh └── templates ├── additional_templates.go └── options_templates.go /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 错误报告 | Bug Report 3 | about: 请详细描述您在使用过程中遇到的问题。| Please provide a detailed description of the issue you encountered during use. 4 | title: "" 5 | labels: ["bug"] 6 | assignees: '' 7 | --- 8 | 9 | 在提交之前请先查找 [已有 issues](https://github.com/chenmingyong0423/go-mongox/issues),避免重复上报。| Before submitting, please search for [existing issues](https://github.com/chenmingyong0423/go-mongox/issues) to avoid duplicate reports. 10 | 11 | 并且确保自己已经 | Also, ensure that you have: 12 | - [ ] 阅读过文档 | Read the documentation 13 | - [ ] 阅读过注释 | Reviewed the comments 14 | - [ ] 阅读过例子 | Looked at the examples 15 | 16 | 17 | ### 您使用的 mongox 版本 | The version of mongox you are using 18 | 19 | ### 您遇到的问题 | The issue you encountered. 20 | 21 | ### 复现步骤 | Steps to reproduce 22 | > 请提供简单的复现代码 | Please provide simple code to reproduce the issue. 23 | 24 | ### 错误日志或者截图 | Error logs or Screenshots 25 | 26 | ### 你排查的结果,或者你觉得可行的修复方案 | Your Findings or Possible Solutions 27 | > 可选。我们希望你能够尽量先排查问题,帮助我们减轻维护负担。这对于你个人能力提升同样是有帮助的。| Optional. We hope you can try to troubleshoot the issue first, helping us to reduce our maintenance burden. This is also beneficial for your personal skill development 28 | 29 | #### 您期望的结果 | Expected Outcome 30 | 31 | ### 你设置的的 Go 环境 | Your Go Environment Setting 32 | > 上传 `go env` 的结果 | Upload the result of `go env` 33 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 功能请求 | Feature Request 3 | about: 请描述您期望的功能 | The features you expect. 4 | title: '[功能请求] | [Feature Request] ' 5 | labels: ['enhancement'] 6 | assignees: '' 7 | --- 8 | 9 | ### 使用场景 | Use Case Scenario 10 | > 请尽可能详细地描述您希望添加的功能。包括其具体用途和实现的效果。| Please describe the feature you would like to be added in as much detail as possible. Include its specific use case and how it would function. 11 | 12 | ### 可行方案 | Feasible Solutions 13 | > 如果你知道有框架提供了类似功能,可以在这里描述,并且给出文档或者例子。 | If you are aware of any frameworks that provide similar features, please describe them here and include documentation or examples. 14 | > 15 | > 如果你有设计思路或者解决方案,请在这里提供。你可以提供多个方案,并且给出自己的选择。| If you have any design ideas or proposed solutions, please share them here. You may provide multiple options and explain your preference. 16 | 17 | ### 其它 | Others 18 | > 任何你觉得有利于解决问题的补充说明 | Any additional information you think may be helpful in solving the problem. 19 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/question.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 问题 | Question 3 | about: 你想问的问题 | The question you want to ask 4 | title: '' 5 | labels: ["question"] 6 | --- 7 | 8 | 在提交之前请先查找 [已有 issues](https://github.com/chenmingyong0423/go-mongox/issues),避免重复上报。| Before submitting, please search for [existing issues](https://github.com/chenmingyong0423/go-mongox/issues) to avoid duplicate reports. 9 | 10 | ### 您使用的 mongox 版本 | The version of mongox you are using 11 | 12 | ### 你的问题 | Your question 13 | 14 | ### 你设置的的 Go 环境 | Your Go Environment Setting 15 | > 上传 `go env` 的结果 | Upload the result of `go env` 16 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/refactor.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 重构请求 | Refactor request 3 | about: 重构已有代码 | Refactor existing code 4 | title: '' 5 | labels: [ refactor ] 6 | assignees: '' 7 | --- 8 | 9 | 在提交之前请先查找 [已有 issues](https://github.com/chenmingyong0423/go-mongox/issues),避免重复上报。| Before submitting, please search for [existing issues](https://github.com/chenmingyong0423/go-mongox/issues) to avoid duplicate reports. 10 | 11 | ### 您使用的 mongox 版本 | The version of mongox you are using 12 | 13 | ### 当前实现缺陷 | Current Implementation Deficiencies 14 | 15 | ### 重构方案 | Refactoring Plan 16 | > 描述可以如何重构,以及重构之后带来的效果,如可读性、性能等方面的提升 | Describe how you can refactor, and the benefits it would bring, such as improvements in readability, performance, etc 17 | 18 | ### 其它 | Others 19 | > 任何你觉得有利于解决问题的补充说明 | Any other information you think would be helpful in addressing the issue 20 | 21 | ### 你设置的的 Go 环境 | Your Go Environment Setting 22 | > 上传 `go env` 的结果 | Upload the result of `go env` 23 | -------------------------------------------------------------------------------- /.github/pre-commit: -------------------------------------------------------------------------------- 1 | # Copyright 2023 chenmingyong0423 2 | 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # 15 | # This script does not handle file names that contain spaces. 16 | 17 | # Pre-commit configuration 18 | 19 | RESULT=$(make check) 20 | printf "Checking in progress...\n" 21 | 22 | if [ -n "$RESULT" ]; then 23 | echo >&2 "[ERROR]: One or more files have changed, please add the changed files to this submission." 24 | exit 1 25 | fi 26 | 27 | exit 0 28 | -------------------------------------------------------------------------------- /.github/pre-push: -------------------------------------------------------------------------------- 1 | # Copyright 2023 chenmingyong0423 2 | 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # 15 | # This script does not handle file names that contain spaces. 16 | 17 | # Pre-push configuration 18 | remote=$1 19 | url=$2 20 | echo >&2 "Try pushing $2 to $1" 21 | 22 | TEST="go test ./... -race -cover -failfast" 23 | 24 | LINTER="golangci-lint run" 25 | 26 | # Run test and return if failed 27 | printf "Running go test..." 28 | $TEST 29 | RESULT=$? 30 | if [ $RESULT -ne 0 ]; then 31 | echo >&2 "$TEST" 32 | echo >&2 "Check code to pass test." 33 | exit 1 34 | fi 35 | 36 | # Run linter and return if failed 37 | printf "Running go linter..." 38 | $LINTER 39 | RESULT=$? 40 | if [ $RESULT -ne 0 ]; then 41 | echo >&2 "$LINTER" 42 | echo >&2 "Check code to pass linter." 43 | exit 1 44 | fi 45 | 46 | exit 0 47 | -------------------------------------------------------------------------------- /.github/workflows/go-fmt.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 chenmingyong0423 2 | 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | name: Format Go code 16 | 17 | on: 18 | push: 19 | branches: [ main, dev] 20 | pull_request: 21 | branches: [ main, dev] 22 | 23 | jobs: 24 | build: 25 | runs-on: ubuntu-latest 26 | steps: 27 | - uses: actions/checkout@v4 28 | - name: Set up Go 29 | uses: actions/setup-go@v5 30 | with: 31 | go-version: ">=1.22.0" 32 | 33 | - name: Install goimports 34 | run: go install golang.org/x/tools/cmd/goimports@latest 35 | 36 | - name: Check 37 | run: | 38 | make check 39 | if [ -n "$(git status --porcelain)" ]; then 40 | echo >&2 "error: Please execute the command → 'make check' in the local warehouse and submit again." 41 | exit 1 42 | fi 43 | -------------------------------------------------------------------------------- /.github/workflows/go.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 chenmingyong0423 2 | 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # This workflow will build a golang project 16 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go 17 | 18 | name: Go 19 | 20 | on: 21 | push: 22 | branches: [ "main", "dev" ] 23 | pull_request: 24 | branches: [ "main", "dev" ] 25 | 26 | jobs: 27 | 28 | build: 29 | runs-on: ubuntu-latest 30 | steps: 31 | - uses: actions/checkout@v4 32 | 33 | - name: Set up Go 34 | uses: actions/setup-go@v5 35 | with: 36 | go-version: '1.22' 37 | 38 | - name: Build 39 | run: go build -v ./... 40 | 41 | - name: Test 42 | run: go test -race -coverprofile=cover.out -v ./... 43 | 44 | - name: Post Coverage 45 | uses: codecov/codecov-action@v4 46 | -------------------------------------------------------------------------------- /.github/workflows/golangci-lint.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 chenmingyong0423 2 | 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | name: golangci-lint 16 | on: 17 | push: 18 | tags: 19 | - v* 20 | branches: 21 | - master 22 | - main 23 | - dev 24 | pull_request: 25 | permissions: 26 | contents: read 27 | # Optional: allow read access to pull request. Use with `only-new-issues` option. 28 | # pull-requests: read 29 | jobs: 30 | golangci: 31 | name: lint 32 | runs-on: ubuntu-latest 33 | steps: 34 | - uses: actions/setup-go@v5 35 | with: 36 | go-version: '1.22' 37 | - uses: actions/checkout@v4 38 | - name: golangci-lint 39 | uses: golangci/golangci-lint-action@v6 40 | with: 41 | # Optional: version of golangci-lint to use in form of v1.2 or v1.2.3 or `latest` to use the latest version 42 | version: latest 43 | 44 | # Optional: working directory, useful for monorepos 45 | # working-directory: somedir 46 | 47 | # Optional: golangci-lint command line arguments. 48 | args: --timeout=10m # --issues-exit-code=0 49 | 50 | # Optional: show only new issues if it's a pull request. The default value is `false`. 51 | only-new-issues: true 52 | 53 | # Optional: if set to true then the all caching functionality will be complete disabled, 54 | # takes precedence over all other caching options. 55 | # skip-cache: true 56 | 57 | # Optional: if set to true then the action don't cache or restore ~/go/pkg. 58 | # skip-pkg-cache: true 59 | 60 | # Optional: if set to true then the action don't cache or restore ~/.cache/go-build. 61 | # skip-build-cache: true 62 | -------------------------------------------------------------------------------- /.github/workflows/stale.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 chenmingyong0423 2 | 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | name: Mark stale issues and pull requests 16 | 17 | on: 18 | schedule: 19 | - cron: "30 1 * * *" 20 | 21 | jobs: 22 | stale: 23 | 24 | runs-on: ubuntu-latest 25 | 26 | steps: 27 | - uses: actions/stale@v9 28 | with: 29 | repo-token: ${{ secrets.GITHUB_TOKEN }} 30 | stale-issue-message: 'This issue is inactive for a long time.' 31 | stale-pr-message: 'This PR is inactive for a long time' 32 | stale-issue-label: 'inactive-issue' 33 | stale-pr-label: 'inactive-pr' 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # If you prefer the allow list template instead of the deny list, see community template: 2 | # https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore 3 | # 4 | # Binaries for programs and plugins 5 | *.exe 6 | *.exe~ 7 | *.dll 8 | *.so 9 | *.dylib 10 | 11 | # Test binary, built with `go test -c` 12 | *.test 13 | 14 | # Output of the go coverage tool, specifically when used with LiteIDE 15 | *.out 16 | 17 | # Dependency directories (remove the comment below to include it) 18 | # vendor/ 19 | 20 | # Go workspace file 21 | go.work 22 | 23 | .idea 24 | 25 | ./.idea -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # 单元测试 2 | .PHONY: ut 3 | ut: 4 | @go test -race ./... 5 | 6 | .PHONY: setup 7 | setup: 8 | @sh ./script/setup.sh 9 | 10 | .PHONY: lint 11 | lint: 12 | golangci-lint run 13 | 14 | .PHONY: fmt 15 | fmt: 16 | @sh ./script/fmt.sh 17 | 18 | .PHONY: tidy 19 | tidy: 20 | @go mod tidy -v 21 | 22 | .PHONY: check 23 | check: 24 | @$(MAKE) --no-print-directory fmt 25 | @$(MAKE) --no-print-directory tidy 26 | 27 | .PHONY: e2e_up 28 | e2e_up: 29 | docker-compose -f script/integration_test_compose.yml up -d 30 | 31 | .PHONY: e2e_down 32 | e2e_down: 33 | docker-compose -f script/integration_test_compose.yml down -v -------------------------------------------------------------------------------- /README-zh_CN.md: -------------------------------------------------------------------------------- 1 |

2 | go-optioner 3 |

4 | 5 | [![GitHub Repo stars](https://img.shields.io/github/stars/chenmingyong0423/go-optioner)](https://github.com/chenmingyong0423/go-optioner/stargazers) 6 | [![GitHub issues](https://img.shields.io/github/issues/chenmingyong0423/go-optioner)](https://github.com/chenmingyong0423/go-optioner/issues) 7 | [![GitHub License](https://img.shields.io/github/license/chenmingyong0423/go-optioner)](https://github.com/chenmingyong0423/go-optioner/blob/main/LICENSE) 8 | [![GitHub release (with filter)](https://img.shields.io/github/v/release/chenmingyong0423/go-optioner)](https://github.com/chenmingyong0423/go-optioner) 9 | [![Go Report Card](https://goreportcard.com/badge/github.com/chenmingyong0423/go-optioner)](https://goreportcard.com/report/github.com/chenmingyong0423/go-optioner) 10 | [![All Contributors](https://img.shields.io/badge/all_contributors-1-orange.svg?style=flat-square)](#contributors-) 11 | 12 | `go-optioner` 是一个在 Go 代码中生成函数选项模式代码的工具。该工具可以根据给定的结构定义自动生成相应的选项代码。 13 | 14 | --- 15 | 16 | [English](./README.md) | 中文简体 17 | 18 | # 安装 19 | - 1、`go install github.com/chenmingyong0423/go-optioner/cmd/optioner@latest` 20 | - 2、执行 `optioner` 命令检查是否安装成功 21 | ``` 22 | > optioner 23 | optioner is a tool for generating functional options pattern. 24 | Usage: 25 | optioner [flags] 26 | Flags: 27 | -type 28 | -output , default: srcDir/opt_xxx_gen.go 29 | -with_prefix , default is With{filed_name}.If specified, such as User, it will generate WithUser{filed_name} 30 | -mode , default: write 31 | there are two available modes: 32 | - write(Write/Overwrite): Overwrites or creates a new file. 33 | - append (Append): Adds to the end of the file. 34 | ``` 35 | 如果你安装成功了,但是提示 `optioner` 命令找不到,请确认是否已将 `$GOPATH/bin` 添加到环境变量中。 36 | # 参数 37 | 使用 `Optioner` 时,你可以通过以下参数定制其行为: 38 | 39 | - `-type`: 指定目标结构体的名称,这是一个必需参数。 40 | - `-output`: 设置生成代码的输出文件路径。这是一个可选参数,默认生成的文件名格式为 `opt_{StructName}_gen.go`,其中 `{StructName}` 是结构体的名称,文件位于当前目录下。 41 | - `-with_prefix`:设置 With{filed_name} 函数的前缀。这是一个可选参数,默认值为 With{filed_name}。如果指定了前缀,例如 User,则生成的函数名将变为 WithUser{filed_name}。 42 | - `-mode`: 定义文件写入方式,接受的值有: 43 | - `write`(写入/覆盖):如果文件已存在,将其覆盖;如果文件不存在,创建新文件。 44 | - `append`(追加):将内容追加到现有文件的末尾。 45 | 46 | # 使用教程 47 | 你可以直接使用 `optioner` 命令生成对应结构体的 `functional options` 代码,也可以使用 `go generate` 进行批量生成。 48 | ## optioner 命令 49 | - 1、首先,假定您已经准备好一个 `Go` 文件,其中包含了您希望生成函数选项模式代码的结构体。在该结构体的字段上,您可以利用 `opt` 标签来标记哪些字段应作为 `New{FieldName}` 函数的必要参数。 50 | ```go 51 | package example 52 | 53 | type User[T any, R any] struct { 54 | Name string `opt:"-"` 55 | NecGenericFiled T `opt:"-"` 56 | Age int 57 | Gender string 58 | GenericFiled R 59 | } 60 | 61 | ``` 62 | 63 | 如果结构体字段使用了 `opt` 标签并将其值设置为 `-`,则该字段成为 `New{FieldName}` 函数的必需参数,同时不会为该字段生成 `With{FieldName}` 函数。 64 | 65 | 注意:必须声明 `package`。 66 | - 2、在包含结构体定义的文件所在目录下,执行 `optioner -type {StructName}` 命令,将 `{StructName}` 替换为实际的结构体名称,例如 `optioner -type User`。此命令执行后,`optioner` 工具会根据结构体定义自动创建默认的 `opt_user_gen.go` 文件,并在其中生成函数选项模式代码。生成的代码结构如下所示: 67 | ```go 68 | // Generated by [optioner] command-line tool; DO NOT EDIT 69 | // If you have any questions, please create issues and submit contributions at: 70 | // https://github.com/chenmingyong0423/go-optioner 71 | 72 | package example 73 | 74 | type UserOption[T any, R any] func(*User[T, R]) 75 | 76 | func NewUser[T any, R any](name string, necGenericFiled T, opts ...UserOption[T, R]) *User[T, R] { 77 | user := &User[T, R]{ 78 | Name: name, 79 | NecGenericFiled: necGenericFiled, 80 | } 81 | 82 | for _, opt := range opts { 83 | opt(user) 84 | } 85 | 86 | return user 87 | } 88 | 89 | func WithAge[T any, R any](age int) UserOption[T, R] { 90 | return func(user *User[T, R]) { 91 | user.Age = age 92 | } 93 | } 94 | 95 | func WithGender[T any, R any](gender string) UserOption[T, R] { 96 | return func(user *User[T, R]) { 97 | user.Gender = gender 98 | } 99 | } 100 | 101 | func WithGenericFiled[T any, R any](genericFiled R) UserOption[T, R] { 102 | return func(user *User[T, R]) { 103 | user.GenericFiled = genericFiled 104 | } 105 | } 106 | 107 | ``` 108 | 109 | 如果需要自定义生成代码的输出文件路径,可以通过指定 `output` 和 `mode` 参数进行配置。 110 | ## go generate 命令 111 | 请注意,在执行 `go generate` 命令之前,确保您的项目已经初始化 `Go Modules` 或正确设置了 `GOPATH`,并且您的项目结构符合 `Go Modules` 或 `GOPATH` 的要求。 112 | 113 | - 1、首先,假定您已经准备好一个 `Go` 文件,其中包含了您希望生成函数选项模式代码的结构体。在该结构体定义之上,添加注释 `//go:generate optioner -type {StructName}`,并把 `{StructName}` 替换为您的实际结构体名称。例如,使用 `//go:generate optioner -type User` 来为 `User` 结构体生成代码。在该结构体的字段上,您可以利用 `opt` 标签来标记哪些字段应作为 `New{FieldName}` 函数的必要参数。 114 | ```go 115 | package example 116 | 117 | //go:generate optioner -type User 118 | type User[T any, R any] struct { 119 | Name string `opt:"-"` 120 | NecGenericFiled T `opt:"-"` 121 | Age int 122 | Gender string 123 | GenericFiled R 124 | } 125 | 126 | ``` 127 | 128 | 如果结构体字段使用了 `opt` 标签并将其值设置为 `-`,则该字段成为 `New{FieldName}` 函数的必需参数,同时不会为该字段生成 `With{FieldName}` 函数。 129 | 130 | 注意:必须声明 `package`。 131 | - 2、在包含结构体定义的文件所在目录下,运行 `go generate` 命令,这个命令将触发 `optioner` 工具,并根据结构体定义自动创建默认的 `opt_user_gen.go` 文件,同时在该文件中生成函数选项模式的代码。生成的代码结构如下所示: 132 | ```go 133 | // Generated by [optioner] command-line tool; DO NOT EDIT 134 | // If you have any questions, please create issues and submit contributions at: 135 | // https://github.com/chenmingyong0423/go-optioner 136 | 137 | package example 138 | 139 | type UserOption[T any, R any] func(*User[T, R]) 140 | 141 | func NewUser[T any, R any](name string, necGenericFiled T, opts ...UserOption[T, R]) *User[T, R] { 142 | user := &User[T, R]{ 143 | Name: name, 144 | NecGenericFiled: necGenericFiled, 145 | } 146 | 147 | for _, opt := range opts { 148 | opt(user) 149 | } 150 | 151 | return user 152 | } 153 | 154 | func WithAge[T any, R any](age int) UserOption[T, R] { 155 | return func(user *User[T, R]) { 156 | user.Age = age 157 | } 158 | } 159 | 160 | func WithGender[T any, R any](gender string) UserOption[T, R] { 161 | return func(user *User[T, R]) { 162 | user.Gender = gender 163 | } 164 | } 165 | 166 | func WithGenericFiled[T any, R any](genericFiled R) UserOption[T, R] { 167 | return func(user *User[T, R]) { 168 | user.GenericFiled = genericFiled 169 | } 170 | } 171 | 172 | ``` 173 | 174 | 如果需要自定义生成代码的输出文件路径,可以修改 `//go:generate optioner -type User` 的内容,通过指定 `output` 和 `mode` 参数进行配置。 175 | # 如何贡献 176 | 我非常欢迎其他人对这个项目做出贡献!如果你有任何问题、改进建议或发现了 bug,请通过提交 issue 来与我们交流。如果你想提交代码贡献,请按照以下步骤: 177 | 178 | 1、`Fork` 这个仓库并克隆到本地。 179 | 180 | 2、创建一个新的分支:`git checkout -b feature/your-feature`。 181 | 182 | 3、在你的分支上进行修改或添加新功能。 183 | 184 | 4、提交你的更改:`git commit -m "描述你的修改"` 185 | 186 | 5、推送到你的 `Fork` 仓库:`git push origin feature/your-feature` 187 | 188 | 6、创建 `Pull` 请求,将你的更改合并到主仓库。 189 | 190 | 请确保您的代码遵循项目的编码风格并通过测试。 191 | # 许可证 192 | 这个项目遵循 [Apache License](https://github.com/chenmingyong0423/go-optioner/blob/main/LICENSE) 许可。 193 | # 联系我们 194 | 如果你有任何问题或意见,可以通过以下方式联系我们: 195 | - 邮件:[chenmingyong5873@126.com] 196 | - GitHub:[https://github.com/chenmingyong0423] 197 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | go-optioner 3 |

4 | 5 | [![GitHub Repo stars](https://img.shields.io/github/stars/chenmingyong0423/go-optioner)](https://github.com/chenmingyong0423/go-optioner/stargazers) 6 | [![GitHub issues](https://img.shields.io/github/issues/chenmingyong0423/go-optioner)](https://github.com/chenmingyong0423/go-optioner/issues) 7 | [![GitHub License](https://img.shields.io/github/license/chenmingyong0423/go-optioner)](https://github.com/chenmingyong0423/go-optioner/blob/main/LICENSE) 8 | [![GitHub release (with filter)](https://img.shields.io/github/v/release/chenmingyong0423/go-optioner)](https://github.com/chenmingyong0423/go-optioner) 9 | [![Go Report Card](https://goreportcard.com/badge/github.com/chenmingyong0423/go-optioner)](https://goreportcard.com/report/github.com/chenmingyong0423/go-optioner) 10 | [![All Contributors](https://img.shields.io/badge/all_contributors-1-orange.svg?style=flat-square)](#contributors-) 11 | 12 | `go-optioner` is a tool for generating functional options pattern in Go code. This tool can automatically generate corresponding options code based on the given struct definition. 13 | 14 | --- 15 | 16 | English | [中文简体](./README-zh_CN.md) 17 | 18 | # Installation 19 | - 1、`go install github.com/chenmingyong0423/go-optioner/cmd/optioner@latest` 20 | - 2、Run the `optioner` command to check if the installation is successful. 21 | ``` 22 | > optioner 23 | optioner is a tool for generating functional options pattern. 24 | Usage: 25 | optioner [flags] 26 | Flags: 27 | -type 28 | -output , default: srcDir/opt_xxx_gen.go 29 | -with_prefix , default is With{filed_name}.If specified, such as User, it will generate WithUser{filed_name} 30 | -mode , default: write 31 | there are two available modes: 32 | - write(Write/Overwrite): Overwrites or creates a new file. 33 | - append (Append): Adds to the end of the file. 34 | ``` 35 | If you install it successfully and the `optioner` command is not found, make sure to add `$GOPATH/bin` to your environment variables. 36 | 37 | # Arguments 38 | When using `Optioner`, you can customize its behavior with the following parameters: 39 | 40 | - `-type`: Specifies the name of the target struct. This is a required parameter. 41 | - `-output`: Sets the output file path for the generated code. This is an optional parameter, with the default file name format being `opt_{StructName}_gen.go`, where `{StructName}` is the name of the struct. The file is placed in the current directory by default. 42 | - `-with_prefix`: Sets the prefix for the `With{field_name}` function. This is an optional parameter with a default value of `With{field_name}`. If a prefix is specified, such as User, the generated function name will become `WithUser{field_name}`. 43 | - `-mode`: Determines the file writing mode, with two available options: 44 | - `write` (Write/Overwrite): Overwrites the file if it exists or creates a new one if it does not. 45 | - `append` (Append): Adds content to the end of an existing file. 46 | 47 | # Usage 48 | 49 | You can directly use the `optioner` command to generate functional options code for the corresponding struct, or you can use `go generate` for bulk generation. 50 | ## Optioner Command 51 | - 1. First, assume you have prepared a Go file containing the struct for which you want to generate functional option pattern code. You can use the `opt` tag on the struct's fields to indicate which fields should be required parameters for the `New{FieldName}` function. 52 | ```go 53 | package example 54 | 55 | type User[T any, R any] struct { 56 | Name string `opt:"-"` 57 | NecGenericFiled T `opt:"-"` 58 | Age int 59 | Gender string 60 | GenericFiled R 61 | } 62 | 63 | ``` 64 | 65 | If a struct field is tagged with `opt` and its value is set to `-`, the field becomes a required parameter for the `New{FieldName}` function, and a `With{FieldName}` function will not be generated for that field. 66 | 67 | Note: A `package` declaration is required. 68 | - 2. In the directory containing the struct definition file, execute the `optioner -type {StructName}` command, replacing `{StructName}` with the actual name of your struct, for example, `optioner -type User`. After running this command, the `optioner` tool automatically creates a default `opt_user_gen.go` file and generates the functional options pattern code within it. The generated code structure is as follows: 69 | ```go 70 | // Generated by [optioner] command-line tool; DO NOT EDIT 71 | // If you have any questions, please create issues and submit contributions at: 72 | // https://github.com/chenmingyong0423/go-optioner 73 | 74 | package example 75 | 76 | type UserOption[T any, R any] func(*User[T, R]) 77 | 78 | func NewUser[T any, R any](name string, necGenericFiled T, opts ...UserOption[T, R]) *User[T, R] { 79 | user := &User[T, R]{ 80 | Name: name, 81 | NecGenericFiled: necGenericFiled, 82 | } 83 | 84 | for _, opt := range opts { 85 | opt(user) 86 | } 87 | 88 | return user 89 | } 90 | 91 | func WithAge[T any, R any](age int) UserOption[T, R] { 92 | return func(user *User[T, R]) { 93 | user.Age = age 94 | } 95 | } 96 | 97 | func WithGender[T any, R any](gender string) UserOption[T, R] { 98 | return func(user *User[T, R]) { 99 | user.Gender = gender 100 | } 101 | } 102 | 103 | func WithGenericFiled[T any, R any](genericFiled R) UserOption[T, R] { 104 | return func(user *User[T, R]) { 105 | user.GenericFiled = genericFiled 106 | } 107 | } 108 | 109 | ``` 110 | 111 | To customize the output file path for the generated code, you can specify the `output` and `mode` parameters. 112 | 113 | ## go generate Command 114 | Please ensure your project has been initialized with Go Modules or is correctly set up with `GOPATH`, and that your project structure complies with the requirements of Go Modules or `GOPATH` before running the `go generate` command. 115 | 116 | - 1. First, assume you have prepared a Go file containing the struct for which you want to generate functional option pattern code. Add the comment `//go:generate optioner -type {StructName}` above the struct definition, replacing `{StructName}` with the actual name of your struct, e.g., use `//go:generate optioner -type User` for generating code for the User struct. You can use the `opt` tag on the struct's fields to indicate which fields should be required parameters for the `New{FieldName}` function. 117 | ```go 118 | package example 119 | 120 | //go:generate optioner -type User 121 | type User[T any, R any] struct { 122 | Name string `opt:"-"` 123 | NecGenericFiled T `opt:"-"` 124 | Age int 125 | Gender string 126 | GenericFiled R 127 | } 128 | 129 | ``` 130 | 131 | If a struct field is tagged with `opt` and its value is set to `-`, the field becomes a required parameter for the `New{FieldName}` function, and a `With{FieldName}` function will not be generated for that field. 132 | 133 | Note: A `package` declaration is required. 134 | - 2. Run the `go generate` command in the directory containing the struct definition file. This command triggers the `optioner` tool and automatically creates a default `opt_user_gen.go` file, generating functional options pattern code within it. The generated code structure is as follows: 135 | ```go 136 | // Generated by [optioner] command-line tool; DO NOT EDIT 137 | // If you have any questions, please create issues and submit contributions at: 138 | // https://github.com/chenmingyong0423/go-optioner 139 | 140 | package example 141 | 142 | type UserOption[T any, R any] func(*User[T, R]) 143 | 144 | func NewUser[T any, R any](name string, necGenericFiled T, opts ...UserOption[T, R]) *User[T, R 145 | 146 | ] { 147 | user := &User[T, R]{ 148 | Name: name, 149 | NecGenericFiled: necGenericFiled, 150 | } 151 | 152 | for _, opt := range opts { 153 | opt(user) 154 | } 155 | 156 | return user 157 | } 158 | 159 | func WithAge[T any, R any](age int) UserOption[T, R] { 160 | return func(user *User[T, R]) { 161 | user.Age = age 162 | } 163 | } 164 | 165 | func WithGender[T any, R any](gender string) UserOption[T, R] { 166 | return func(user *User[T, R]) { 167 | user.Gender = gender 168 | } 169 | } 170 | 171 | func WithGenericFiled[T any, R any](genericFiled R) UserOption[T, R] { 172 | return func(user *User[T, R]) { 173 | user.GenericFiled = genericFiled 174 | } 175 | } 176 | 177 | ``` 178 | 179 | To customize the output file path for the generated code, you can modify the content of `//go:generate optioner -type User` by specifying the `output` and `mode` parameters. 180 | # How to Contribute 181 | We welcome contributions from others! If you have any questions, suggestions for improvement, or have found a bug, please create an issue to discuss it with us. If you want to contribute code changes, please follow these steps: 182 | 183 | 1、Fork this repository and clone it to your local machine. 184 | 185 | 2、Create a new branch: `git checkout -b feature/your-feature`. 186 | 187 | 3、Make your modifications or add new features on your branch. 188 | 189 | 4、Commit your changes: `git commit -m "Describe your changes"`. 190 | 191 | 5、Push to your Fork repository: 1git push origin feature/your-feature1. 192 | 193 | 6、Create a Pull Request to merge your changes into the main repository. 194 | 195 | Please ensure that your code follows the project's coding style and passes the tests. 196 | 197 | # License 198 | This project is licensed under the [Apache License](https://github.com/chenmingyong0423/go-optioner/blob/main/LICENSE). 199 | 200 | # Contact Us 201 | If you have any questions or suggestions, you can contact us through the following methods: 202 | - Email: [chenmingyong5873@126.com] 203 | - GitHub: [https://github.com/chenmingyong0423] 204 | -------------------------------------------------------------------------------- /cmd/optioner/main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 chenmingyong0423 2 | 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package main 16 | 17 | import ( 18 | "flag" 19 | "fmt" 20 | "log" 21 | "os" 22 | 23 | "github.com/chenmingyong0423/gkit/stringx" 24 | "github.com/chenmingyong0423/go-optioner/options" 25 | ) 26 | 27 | type ModeValue struct { 28 | value string 29 | validValues []string 30 | } 31 | 32 | func (m *ModeValue) String() string { 33 | return m.value 34 | } 35 | 36 | func (m *ModeValue) Set(s string) error { 37 | for _, v := range m.validValues { 38 | if s == v { 39 | m.value = s 40 | return nil 41 | } 42 | } 43 | return fmt.Errorf("invalid value %q for mode, valid values are: %v", s, m.validValues) 44 | } 45 | 46 | var ( 47 | outputMode = ModeValue{ 48 | value: "write", 49 | validValues: []string{"write", "append"}, 50 | } 51 | structTypeName = flag.String("type", "", "Struct type name of the functional options struct.") 52 | output = flag.String("output", "", "Output file name, default: srcDir/opt__gen.go") 53 | // 生成 With{filed_name} 时的前缀,例如 WithName。如果指定了前缀 User,则生成 WithUserName 54 | withPrefix string 55 | g = options.NewGenerator() 56 | ) 57 | 58 | func usage() { 59 | fmt.Fprintf(os.Stderr, "optioner is a tool for generating functional options pattern.\n") 60 | fmt.Fprintf(os.Stderr, "Usage: \n") 61 | fmt.Fprintf(os.Stderr, "\t optioner [flags]\n") 62 | fmt.Fprintf(os.Stderr, "Flags:\n") 63 | fmt.Fprintf(os.Stderr, "\t -type \n") 64 | fmt.Fprintf(os.Stderr, "\t -output , default: srcDir/opt_xxx_gen.go\n") 65 | fmt.Fprintf(os.Stderr, "\t -with_prefix , default is With{filed_name}.If specified, such as User, it will generate WithUser{filed_name}\n") 66 | fmt.Fprintf(os.Stderr, "\t -mode , default: write\n") 67 | fmt.Fprintf(os.Stderr, "\t there are two available modes:\n") 68 | fmt.Fprintf(os.Stderr, "\t\t - write(Write/Overwrite): Overwrites or creates a new file.\n") 69 | fmt.Fprintf(os.Stderr, "\t\t - append (Append): Adds to the end of the file.\n") 70 | 71 | } 72 | 73 | func main() { 74 | flag.Var(&outputMode, "mode", "The file writing mode, default: write") 75 | flag.StringVar(&withPrefix, "with_prefix", "", "The prefix of the With{filed_name} function, default is With{filed_name}.If specified, such as User, it will generate WithUser{filed_name}") 76 | flag.Usage = usage 77 | flag.Parse() 78 | if len(*structTypeName) == 0 { 79 | flag.Usage() 80 | os.Exit(1) 81 | } 82 | g.StructInfo.StructName = *structTypeName 83 | g.StructInfo.NewStructName = stringx.BigCamelToSmallCamel(*structTypeName) 84 | g.SetOutPath(output) 85 | g.SetMod(outputMode.value) 86 | g.SetWithPrefix(withPrefix) 87 | 88 | g.GeneratingOptions() 89 | if !g.Found { 90 | log.Printf("Target \"[%s]\" is not be found\n", g.StructInfo.StructName) 91 | os.Exit(1) 92 | } 93 | g.GenerateCodeByTemplate() 94 | g.OutputToFile() 95 | } 96 | -------------------------------------------------------------------------------- /example/examples.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 chenmingyong0423 2 | 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package example 16 | 17 | import ( 18 | "context" 19 | 20 | "github.com/chenmingyong0423/go-optioner/example/third_party" 21 | ) 22 | 23 | // 用于添加 "context" 导包信息,以便在生成代码时判断是否成功去除该无用包信息 24 | var _ context.Context 25 | 26 | type Embedded struct{} 27 | 28 | type Embedded2 struct{} 29 | 30 | type Embedded3 struct{} 31 | 32 | type Embedded4 struct{} 33 | 34 | type Embedded5 struct{} 35 | 36 | type Embedded6 struct{} 37 | 38 | type Embedded7 struct{} 39 | 40 | type Embedded8 struct{} 41 | 42 | //go:generate go run ../cmd/optioner/main.go -type GenericExample -with_prefix Example 43 | //go:generate go run ../cmd/optioner/main.go -type GenericExample -output ./generic_example_option.go 44 | //go:generate go run ../cmd/optioner/main.go -type GenericExample -mode append -output ./additional_generic_example_option.go 45 | type GenericExample[T any, U comparable, V ~int] struct { 46 | A T `opt:"-"` 47 | B U 48 | C V 49 | D string 50 | } 51 | 52 | //go:generate go run ../cmd/optioner/main.go -type User 53 | //go:generate go run ../cmd/optioner/main.go -type User -output ./user_option.go 54 | //go:generate go run ../cmd/optioner/main.go -type User -mode append -output ./additional_user_option.go 55 | type User struct { 56 | Embedded `opt:"-"` 57 | *Embedded2 `opt:"-"` 58 | E3 Embedded3 `opt:"-"` 59 | E4 *Embedded4 `opt:"-"` 60 | Embedded5 61 | *Embedded6 62 | E7 Embedded7 63 | E8 *Embedded8 64 | Username string 65 | Email string 66 | Address // combined struct 67 | ArrayField [4]int 68 | SliceField []int 69 | ThirdPartyField third_party.ThirdParty 70 | MapField map[string]int 71 | PtrField *int 72 | EmptyStructFiled struct{} 73 | SimpleFuncField func() 74 | ComplexFuncField func(a int) 75 | ComplexFuncFieldV2 func() int 76 | ComplexFuncFieldV3 func(a int) int 77 | ComplexFuncFieldV4 func(a int) (int, error) 78 | ChanField chan int 79 | error // interface 80 | } 81 | 82 | type Address struct { 83 | Street string 84 | City string 85 | } 86 | -------------------------------------------------------------------------------- /example/third_party/types.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 chenmingyong0423 2 | 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package third_party 16 | 17 | type ThirdParty struct { 18 | Name string 19 | } 20 | -------------------------------------------------------------------------------- /example/user_option.go: -------------------------------------------------------------------------------- 1 | // Generated by [optioner] command-line tool; DO NOT EDIT 2 | // If you have any questions, please create issues and submit contributions at: 3 | // https://github.com/chenmingyong0423/go-optioner 4 | 5 | package example 6 | 7 | import "github.com/chenmingyong0423/go-optioner/example/third_party" 8 | 9 | type UserOption func(*User) 10 | 11 | func NewUser(embedded Embedded, embedded2 *Embedded2, e3 Embedded3, e4 *Embedded4, opts ...UserOption) *User { 12 | user := &User{ 13 | Embedded: embedded, 14 | Embedded2: embedded2, 15 | E3: e3, 16 | E4: e4, 17 | } 18 | 19 | for _, opt := range opts { 20 | opt(user) 21 | } 22 | 23 | return user 24 | } 25 | 26 | func WithEmbedded5(embedded5 Embedded5) UserOption { 27 | return func(user *User) { 28 | user.Embedded5 = embedded5 29 | } 30 | } 31 | 32 | func WithEmbedded6(embedded6 *Embedded6) UserOption { 33 | return func(user *User) { 34 | user.Embedded6 = embedded6 35 | } 36 | } 37 | 38 | func WithE7(e7 Embedded7) UserOption { 39 | return func(user *User) { 40 | user.E7 = e7 41 | } 42 | } 43 | 44 | func WithE8(e8 *Embedded8) UserOption { 45 | return func(user *User) { 46 | user.E8 = e8 47 | } 48 | } 49 | 50 | func WithUsername(username string) UserOption { 51 | return func(user *User) { 52 | user.Username = username 53 | } 54 | } 55 | 56 | func WithEmail(email string) UserOption { 57 | return func(user *User) { 58 | user.Email = email 59 | } 60 | } 61 | 62 | func WithAddress(address Address) UserOption { 63 | return func(user *User) { 64 | user.Address = address 65 | } 66 | } 67 | 68 | func WithArrayField(arrayField [4]int) UserOption { 69 | return func(user *User) { 70 | user.ArrayField = arrayField 71 | } 72 | } 73 | 74 | func WithSliceField(sliceField []int) UserOption { 75 | return func(user *User) { 76 | user.SliceField = sliceField 77 | } 78 | } 79 | 80 | func WithThirdPartyField(thirdPartyField third_party.ThirdParty) UserOption { 81 | return func(user *User) { 82 | user.ThirdPartyField = thirdPartyField 83 | } 84 | } 85 | 86 | func WithMapField(mapField map[string]int) UserOption { 87 | return func(user *User) { 88 | user.MapField = mapField 89 | } 90 | } 91 | 92 | func WithPtrField(ptrField *int) UserOption { 93 | return func(user *User) { 94 | user.PtrField = ptrField 95 | } 96 | } 97 | 98 | func WithEmptyStructFiled(emptyStructFiled struct{}) UserOption { 99 | return func(user *User) { 100 | user.EmptyStructFiled = emptyStructFiled 101 | } 102 | } 103 | 104 | func WithSimpleFuncField(simpleFuncField func()) UserOption { 105 | return func(user *User) { 106 | user.SimpleFuncField = simpleFuncField 107 | } 108 | } 109 | 110 | func WithComplexFuncField(complexFuncField func(a int)) UserOption { 111 | return func(user *User) { 112 | user.ComplexFuncField = complexFuncField 113 | } 114 | } 115 | 116 | func WithComplexFuncFieldV2(complexFuncFieldV2 func() int) UserOption { 117 | return func(user *User) { 118 | user.ComplexFuncFieldV2 = complexFuncFieldV2 119 | } 120 | } 121 | 122 | func WithComplexFuncFieldV3(complexFuncFieldV3 func(a int) int) UserOption { 123 | return func(user *User) { 124 | user.ComplexFuncFieldV3 = complexFuncFieldV3 125 | } 126 | } 127 | 128 | func WithComplexFuncFieldV4(complexFuncFieldV4 func(a int) (int, error)) UserOption { 129 | return func(user *User) { 130 | user.ComplexFuncFieldV4 = complexFuncFieldV4 131 | } 132 | } 133 | 134 | func WithChanField(chanField chan int) UserOption { 135 | return func(user *User) { 136 | user.ChanField = chanField 137 | } 138 | } 139 | 140 | func WithError(error error) UserOption { 141 | return func(user *User) { 142 | user.error = error 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/chenmingyong0423/go-optioner 2 | 3 | go 1.21.0 4 | 5 | require ( 6 | github.com/chenmingyong0423/gkit v0.5.0 7 | golang.org/x/tools v0.19.0 8 | ) 9 | 10 | require golang.org/x/mod v0.16.0 // indirect 11 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/chenmingyong0423/gkit v0.5.0 h1:efthAjro7c5ZwHqMUV0+0CFZZFgsbthYnnUrfsSMdx4= 2 | github.com/chenmingyong0423/gkit v0.5.0/go.mod h1:9MKV0/B3MiBCGf4fssogftFbNIGJk9a4ffm/o3+pkHw= 3 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 4 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 5 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 6 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 7 | github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= 8 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 9 | golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic= 10 | golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= 11 | golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw= 12 | golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc= 13 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 14 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 15 | -------------------------------------------------------------------------------- /options/options_generator.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 chenmingyong0423 2 | 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package options 16 | 17 | import ( 18 | "bytes" 19 | "fmt" 20 | "go/ast" 21 | "go/build" 22 | "go/parser" 23 | "go/token" 24 | "html/template" 25 | "log" 26 | "os" 27 | "path/filepath" 28 | "reflect" 29 | "strings" 30 | 31 | "github.com/chenmingyong0423/gkit/stringx" 32 | "github.com/chenmingyong0423/go-optioner/templates" 33 | "golang.org/x/tools/imports" 34 | ) 35 | 36 | type Generator struct { 37 | StructInfo *StructInfo 38 | outPath string 39 | mode string 40 | 41 | code bytes.Buffer 42 | header bytes.Buffer 43 | Found bool 44 | } 45 | 46 | func NewGenerator() *Generator { 47 | return &Generator{ 48 | StructInfo: &StructInfo{ 49 | Fields: make([]FieldInfo, 0), 50 | OptionalFields: make([]FieldInfo, 0), 51 | }, 52 | } 53 | } 54 | 55 | type FieldInfo struct { 56 | Name string 57 | Type string 58 | } 59 | 60 | type StructInfo struct { 61 | PackageName string 62 | StructName string 63 | NewStructName string 64 | Fields []FieldInfo 65 | OptionalFields []FieldInfo 66 | GenericParams []FieldInfo 67 | 68 | Imports []string 69 | 70 | WithPrefix string 71 | } 72 | 73 | func (g *Generator) GeneratingOptions() { 74 | pkg, err := build.Default.ImportDir(".", 0) 75 | if err != nil { 76 | log.Fatalf("Processsing directory failed: %s", err.Error()) 77 | } 78 | for _, file := range pkg.GoFiles { 79 | if found := g.parseStruct(file); found { 80 | g.Found = found 81 | break 82 | } 83 | } 84 | } 85 | 86 | func (g *Generator) parseStruct(fileName string) bool { 87 | fSet := token.NewFileSet() 88 | file, err := parser.ParseFile(fSet, fileName, nil, 0) 89 | if err != nil { 90 | log.Fatal(err) 91 | } 92 | 93 | g.StructInfo.PackageName = file.Name.Name 94 | 95 | for _, decl := range file.Decls { 96 | genDecl, ok := decl.(*ast.GenDecl) 97 | if !ok || genDecl.Tok != token.TYPE { 98 | continue 99 | } 100 | for _, spec := range genDecl.Specs { 101 | typeSpec, ok := spec.(*ast.TypeSpec) 102 | if !ok { 103 | continue 104 | } 105 | if typeSpec.Name.String() != g.StructInfo.StructName { 106 | continue 107 | } 108 | if structDecl, ok := typeSpec.Type.(*ast.StructType); ok { 109 | log.Printf("Generating Struct \"%s\" \n", g.StructInfo.StructName) 110 | if typeSpec.TypeParams != nil { 111 | log.Println("This is a struct which contains generic type:", typeSpec.Name) 112 | for _, param := range typeSpec.TypeParams.List { 113 | for _, name := range param.Names { 114 | typ := g.getTypeName(param.Type) 115 | g.StructInfo.GenericParams = append(g.StructInfo.GenericParams, FieldInfo{ 116 | Name: name.Name, 117 | Type: typ, 118 | }) 119 | } 120 | } 121 | } 122 | for _, field := range structDecl.Fields.List { 123 | fieldName := "" 124 | if len(field.Names) == 0 { 125 | if ident, ok := field.Type.(*ast.Ident); ok { // combined struct 126 | fieldName = ident.Name 127 | } else if starExpr, ok := field.Type.(*ast.StarExpr); ok { 128 | if ident2, ok := starExpr.X.(*ast.Ident); ok { // combined struct 129 | fieldName = ident2.Name 130 | } else { 131 | continue 132 | } 133 | } else { 134 | continue 135 | } 136 | } else { 137 | fieldName = field.Names[0].Name 138 | } 139 | optionIgnore := false 140 | 141 | fieldType := g.getTypeName(field.Type) 142 | if field.Tag != nil { 143 | tags := strings.Replace(field.Tag.Value, "`", "", -1) 144 | tag := reflect.StructTag(tags).Get("opt") 145 | if tag == "-" { 146 | g.StructInfo.Fields = append(g.StructInfo.Fields, FieldInfo{ 147 | Name: fieldName, 148 | Type: fieldType, 149 | }) 150 | optionIgnore = true 151 | } 152 | } 153 | if !optionIgnore { 154 | g.StructInfo.OptionalFields = append(g.StructInfo.OptionalFields, FieldInfo{ 155 | Name: fieldName, 156 | Type: fieldType, 157 | }) 158 | } 159 | } 160 | return true 161 | } else { 162 | log.Fatalf(fmt.Sprintf("Target[%s] type is not a struct", g.StructInfo.StructName)) 163 | } 164 | } 165 | } 166 | return false 167 | } 168 | 169 | func (g *Generator) GenerateCodeByTemplate() { 170 | var ( 171 | headerTmpl *template.Template 172 | tmpl *template.Template 173 | err error 174 | ) 175 | if g.mode == "append" { 176 | headerTmpl, err = template.New("header_options").Parse(templates.GeneratedHeader) 177 | if err != nil { 178 | log.Fatal("Failed to parse header template:", err) 179 | } 180 | err = headerTmpl.Execute(&g.header, g.StructInfo) 181 | if err != nil { 182 | log.Fatal(err) 183 | } 184 | } 185 | tmpl = template.New("options").Funcs(template.FuncMap{"bigCamelToSmallCamel": stringx.BigCamelToSmallCamel, "capitalizeFirstLetter": stringx.CapitalizeFirstLetter}) 186 | if g.mode == "write" { 187 | tmpl, err = tmpl.Parse(templates.OptionsTemplateCode) 188 | } else { 189 | tmpl, err = tmpl.Parse(templates.AdditionalTemplateCode) 190 | } 191 | if err != nil { 192 | log.Fatal("Failed to parse template:", err) 193 | } 194 | 195 | err = tmpl.Execute(&g.code, g.StructInfo) 196 | if err != nil { 197 | log.Fatal(err) 198 | } 199 | } 200 | 201 | func (g *Generator) OutputToFile() { 202 | var src []byte 203 | if g.mode == "write" { 204 | dir := filepath.Dir(g.outPath) 205 | err := os.MkdirAll(dir, os.ModePerm) 206 | if err != nil { 207 | log.Fatal("mkdir failed, error: ", err) 208 | } 209 | src = g.forMart() 210 | } else { 211 | readFile, err := os.ReadFile(g.outPath) 212 | if err != nil { 213 | log.Fatal("Open the specified file failed, error: ", err) 214 | } 215 | header := g.header.Bytes() 216 | if !bytes.HasPrefix(readFile, header) { 217 | readFile = append(header, readFile...) 218 | } 219 | readFile = append(readFile, g.code.Bytes()...) 220 | src, err = imports.Process("", readFile, nil) 221 | if err != nil { 222 | log.Fatal("Failed to format the generated code: ", err, ", the grammar of the specified file code maybe incorrect.") 223 | } 224 | } 225 | err := os.WriteFile(g.outPath, src, 0644) 226 | if err != nil { 227 | log.Fatal("write file failed, error: ", err) 228 | } 229 | fmt.Printf("Generating Functional Options Code Successfully.\nOut: %s\n", g.outPath) 230 | } 231 | 232 | func (g *Generator) forMart() []byte { 233 | source, err := imports.Process("", g.code.Bytes(), nil) 234 | if err != nil { 235 | return nil 236 | } 237 | return source 238 | } 239 | 240 | func (g *Generator) SetOutPath(outPath *string) { 241 | fileName := fmt.Sprintf("opt_%s_gen.go", stringx.CamelToSnake(g.StructInfo.StructName)) 242 | if len(*outPath) > 0 { 243 | g.outPath = *outPath 244 | } else { 245 | g.outPath = fileName 246 | } 247 | } 248 | 249 | func (g *Generator) getTypeName(expr ast.Expr) string { 250 | switch t := expr.(type) { 251 | case *ast.Ident: 252 | return t.Name 253 | case *ast.SelectorExpr: 254 | return fmt.Sprintf("%s.%s", g.getTypeName(t.X), t.Sel.Name) 255 | case *ast.ArrayType: 256 | if t.Len == nil { 257 | return "[]" + g.getTypeName(t.Elt) 258 | } 259 | if basicLit, ok := t.Len.(*ast.BasicLit); ok && basicLit.Kind == token.INT { 260 | return "[" + basicLit.Value + "]" + g.getTypeName(t.Elt) 261 | } else { 262 | log.Fatalf("Array len error: %T", t) 263 | return "" 264 | } 265 | case *ast.MapType: 266 | return fmt.Sprintf("map[%s]%s", g.getTypeName(t.Key), g.getTypeName(t.Value)) 267 | case *ast.StarExpr: 268 | return "*" + g.getTypeName(t.X) 269 | //case *ast.InterfaceType: 270 | // return "" // ignore 271 | case *ast.StructType: 272 | return "struct{}" 273 | case *ast.FuncType: 274 | return g.parseFuncType(t) 275 | case *ast.ChanType: 276 | return "chan " + g.getTypeName(t.Value) 277 | case *ast.UnaryExpr: 278 | return "~" + g.getTypeName(t.X) 279 | default: 280 | log.Fatalf("Unsupported type for field: %T", t) 281 | return "" 282 | } 283 | } 284 | 285 | func (g *Generator) parseFuncType(f *ast.FuncType) string { 286 | var params, results []string 287 | if f.Params != nil { 288 | for _, field := range f.Params.List { 289 | paramType := g.getTypeName(field.Type) 290 | for _, name := range field.Names { 291 | params = append(params, fmt.Sprintf("%s %s", name.Name, paramType)) 292 | } 293 | } 294 | } 295 | 296 | if f.Results != nil { 297 | for _, field := range f.Results.List { 298 | resultType := g.getTypeName(field.Type) 299 | if len(field.Names) > 0 { 300 | for _, name := range field.Names { 301 | results = append(results, fmt.Sprintf("%s %s", name.Name, resultType)) 302 | } 303 | } else { 304 | results = append(results, resultType) 305 | } 306 | } 307 | } 308 | 309 | if len(results) == 1 { 310 | return fmt.Sprintf("func(%s) %s", strings.Join(params, ", "), results[0]) 311 | } 312 | return fmt.Sprintf("func(%s) (%s)", strings.Join(params, ", "), strings.Join(results, ", ")) 313 | } 314 | 315 | func (g *Generator) SetMod(mode string) { 316 | g.mode = mode 317 | } 318 | 319 | func (g *Generator) SetWithPrefix(withPrefix string) { 320 | g.StructInfo.WithPrefix = withPrefix 321 | } 322 | -------------------------------------------------------------------------------- /script/fmt.sh: -------------------------------------------------------------------------------- 1 | # Copyright 2023 chenmingyong0423 2 | 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | #!/bin/sh 15 | 16 | # shellcheck disable=SC2044 17 | for item in $(find . -type f -name '*.go' -not -path './.idea/*'); do 18 | goimports -l -w "$item"; 19 | done 20 | -------------------------------------------------------------------------------- /script/setup.sh: -------------------------------------------------------------------------------- 1 | # Copyright 2023 chenmingyong0423 2 | 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | SOURCE_COMMIT=.github/pre-commit 16 | TARGET_COMMIT=.git/hooks/pre-commit 17 | SOURCE_PUSH=.github/pre-push 18 | TARGET_PUSH=.git/hooks/pre-push 19 | 20 | # copy pre-commit file if not exist. 21 | if [ ! -f $TARGET_COMMIT ]; then 22 | echo "set git pre-commit hooks..." 23 | cp $SOURCE_COMMIT $TARGET_COMMIT 24 | fi 25 | 26 | # copy pre-push file if not exist. 27 | if [ ! -f $TARGET_PUSH ]; then 28 | echo "set git pre-push hooks..." 29 | cp $SOURCE_PUSH $TARGET_PUSH 30 | fi 31 | 32 | # add permission to TARGET_PUSH and TARGET_COMMIT file. 33 | test -x $TARGET_PUSH || chmod +x $TARGET_PUSH 34 | test -x $TARGET_COMMIT || chmod +x $TARGET_COMMIT 35 | 36 | echo "install golangci-lint..." 37 | go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest 38 | 39 | echo "install goimports..." 40 | go install golang.org/x/tools/cmd/goimports@latest 41 | 42 | echo "go mod tidy" 43 | go mod tidy 44 | -------------------------------------------------------------------------------- /templates/additional_templates.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 chenmingyong0423 2 | 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package templates 16 | 17 | const GeneratedHeader = `// Generated by [optioner] command-line tool; DO NOT EDIT 18 | // If you have any questions, please create issues and submit contributions at: 19 | // https://github.com/chenmingyong0423/go-optioner 20 | 21 | ` 22 | 23 | const AdditionalTemplateCode = ` 24 | 25 | type {{ .StructName }}Option{{if .GenericParams}}[{{range $index, $param := .GenericParams}}{{if $index}}, {{end}}{{$param.Name}} {{$param.Type}}{{end}}]{{end}} func(*{{ .StructName }}{{if .GenericParams}}[{{range $index, $param := .GenericParams}}{{if $index}}, {{end}}{{$param.Name}}{{end}}]{{end}}) 26 | 27 | func New{{ .StructName }}{{if .GenericParams}}[{{range $index, $param := .GenericParams}}{{if $index}}, {{end}}{{$param.Name}} {{$param.Type}}{{end}}]{{end}}({{ range $index, $field := .Fields }}{{ $field.Name | bigCamelToSmallCamel }} {{ $field.Type }},{{ end }} opts ...{{ .StructName }}Option{{if .GenericParams}}[{{range $index, $param := .GenericParams}}{{if $index}}, {{end}}{{$param.Name}}{{end}}]{{end}}) *{{ .StructName }}{{if .GenericParams}}[{{range $index, $param := .GenericParams}}{{if $index}}, {{end}}{{$param.Name}}{{end}}]{{end}} { 28 | {{ .NewStructName }} := &{{ .StructName }}{{if .GenericParams}}[{{range $index, $param := .GenericParams}}{{if $index}}, {{end}}{{$param.Name}}{{end}}]{{end}}{ 29 | {{ range $index, $field := .Fields }}{{ $field.Name }}: {{ $field.Name | bigCamelToSmallCamel }}, 30 | {{ end }} 31 | } 32 | 33 | for _, opt := range opts { 34 | opt({{ .NewStructName }}) 35 | } 36 | 37 | return {{ .NewStructName }} 38 | } 39 | 40 | {{ if .OptionalFields }} 41 | {{ range $field := .OptionalFields }} 42 | func With{{ $.WithPrefix }}{{ $field.Name | capitalizeFirstLetter }}{{if $.GenericParams}}[{{range $index, $param := $.GenericParams}}{{if $index}}, {{end}}{{$param.Name}} {{$param.Type}}{{end}}]{{end}}({{ $field.Name | bigCamelToSmallCamel }} {{ $field.Type }}) {{ $.StructName }}Option{{if $.GenericParams}}[{{range $index, $param := $.GenericParams}}{{if $index}}, {{end}}{{$param.Name}}{{end}}]{{end}} { 43 | return func({{ $.NewStructName | bigCamelToSmallCamel }} *{{ $.StructName }}{{if $.GenericParams}}[{{range $index, $param := $.GenericParams}}{{if $index}}, {{end}}{{$param.Name}}{{end}}]{{end}}) { 44 | {{ $.NewStructName }}.{{ $field.Name }} = {{ $field.Name | bigCamelToSmallCamel }} 45 | } 46 | } 47 | {{ end }} 48 | {{ end }} 49 | ` 50 | -------------------------------------------------------------------------------- /templates/options_templates.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 chenmingyong0423 2 | 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package templates 16 | 17 | const OptionsTemplateCode = ` 18 | // Generated by [optioner] command-line tool; DO NOT EDIT 19 | // If you have any questions, please create issues and submit contributions at: 20 | // https://github.com/chenmingyong0423/go-optioner 21 | 22 | package {{ .PackageName }} 23 | 24 | type {{ .StructName }}Option{{if .GenericParams}}[{{range $index, $param := .GenericParams}}{{if $index}}, {{end}}{{$param.Name}} {{$param.Type}}{{end}}]{{end}} func(*{{ .StructName }}{{if .GenericParams}}[{{range $index, $param := .GenericParams}}{{if $index}}, {{end}}{{$param.Name}}{{end}}]{{end}}) 25 | 26 | func New{{ .StructName }}{{if .GenericParams}}[{{range $index, $param := .GenericParams}}{{if $index}}, {{end}}{{$param.Name}} {{$param.Type}}{{end}}]{{end}}({{ range $index, $field := .Fields }}{{ $field.Name | bigCamelToSmallCamel }} {{ $field.Type }},{{ end }} opts ...{{ .StructName }}Option{{if .GenericParams}}[{{range $index, $param := .GenericParams}}{{if $index}}, {{end}}{{$param.Name}}{{end}}]{{end}}) *{{ .StructName }}{{if .GenericParams}}[{{range $index, $param := .GenericParams}}{{if $index}}, {{end}}{{$param.Name}}{{end}}]{{end}} { 27 | {{ .NewStructName }} := &{{ .StructName }}{{if .GenericParams}}[{{range $index, $param := .GenericParams}}{{if $index}}, {{end}}{{$param.Name}}{{end}}]{{end}}{ 28 | {{ range $index, $field := .Fields }}{{ $field.Name }}: {{ $field.Name | bigCamelToSmallCamel }}, 29 | {{ end }} 30 | } 31 | 32 | for _, opt := range opts { 33 | opt({{ .NewStructName }}) 34 | } 35 | 36 | return {{ .NewStructName }} 37 | } 38 | 39 | {{ if .OptionalFields }} 40 | {{ range $field := .OptionalFields }} 41 | func With{{ $.WithPrefix }}{{ $field.Name | capitalizeFirstLetter }}{{if $.GenericParams}}[{{range $index, $param := $.GenericParams}}{{if $index}}, {{end}}{{$param.Name}} {{$param.Type}}{{end}}]{{end}}({{ $field.Name | bigCamelToSmallCamel }} {{ $field.Type }}) {{ $.StructName }}Option{{if $.GenericParams}}[{{range $index, $param := $.GenericParams}}{{if $index}}, {{end}}{{$param.Name}}{{end}}]{{end}} { 42 | return func({{ $.NewStructName }} *{{ $.StructName }}{{if $.GenericParams}}[{{range $index, $param := $.GenericParams}}{{if $index}}, {{end}}{{$param.Name}}{{end}}]{{end}}) { 43 | {{ $.NewStructName }}.{{ $field.Name }} = {{ $field.Name | bigCamelToSmallCamel }} 44 | } 45 | } 46 | {{ end }} 47 | {{ end }} 48 | ` 49 | --------------------------------------------------------------------------------