{"id":18310110,"url":"https://github.com/kazhuravlev/options-gen","last_synced_at":"2025-04-06T06:09:43.249Z","repository":{"id":41410874,"uuid":"194243834","full_name":"kazhuravlev/options-gen","owner":"kazhuravlev","description":"Codegen for functional options in go projects","archived":false,"fork":false,"pushed_at":"2024-12-15T14:10:56.000Z","size":249,"stargazers_count":84,"open_issues_count":4,"forks_count":7,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-30T05:06:12.170Z","etag":null,"topics":["codegen","configuration","go","go-generate","go-lib","golang","lib","options","tool"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kazhuravlev.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-06-28T09:11:21.000Z","updated_at":"2025-02-08T08:31:51.000Z","dependencies_parsed_at":"2024-01-08T16:09:17.524Z","dependency_job_id":"410f3e25-8c47-48f1-ab34-a1c74613c571","html_url":"https://github.com/kazhuravlev/options-gen","commit_stats":null,"previous_names":[],"tags_count":44,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kazhuravlev%2Foptions-gen","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kazhuravlev%2Foptions-gen/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kazhuravlev%2Foptions-gen/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kazhuravlev%2Foptions-gen/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kazhuravlev","download_url":"https://codeload.github.com/kazhuravlev/options-gen/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247441053,"owners_count":20939239,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["codegen","configuration","go","go-generate","go-lib","golang","lib","options","tool"],"created_at":"2024-11-05T16:13:15.891Z","updated_at":"2025-04-06T06:09:43.231Z","avatar_url":"https://github.com/kazhuravlev.png","language":"Go","funding_links":[],"categories":["Go Generate Tools","Go 生成工具"],"sub_categories":["Routers","Search and Analytic Databases","路由器"],"readme":"# options-gen\n\n[![Go Reference](https://pkg.go.dev/badge/github.com/kazhuravlev/options-gen.svg)](https://pkg.go.dev/github.com/kazhuravlev/options-gen)\n[![License](https://img.shields.io/github/license/kazhuravlev/options-gen?color=blue)](https://github.com/kazhuravlev/options-gen/blob/master/LICENSE)\n[![Build Status](https://github.com/kazhuravlev/options-gen/actions/workflows/go.yml/badge.svg?branch=master)](https://github.com/kazhuravlev/options-gen/actions/workflows/go.yml?query=branch%3Amaster)\n[![Go Report Card](https://goreportcard.com/badge/github.com/kazhuravlev/options-gen)](https://goreportcard.com/report/github.com/kazhuravlev/options-gen)\n[![CodeCov](https://codecov.io/gh/kazhuravlev/options-gen/branch/master/graph/badge.svg?token=tNKcOjlxLo)](https://codecov.io/gh/kazhuravlev/options-gen)\n[![Mentioned in Awesome Go](https://awesome.re/mentioned-badge.svg)](https://github.com/avelino/awesome-go#go-generate-tools)\n\nCode-generator that allows you to create a functional options like\n[Dave Cheney's post](https://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis).\n\nGenerate the options for your service/client/etc. All that you need is to define a\nstruct with fields, that can be applied as Option then embed this struct into\nyours.\n\n## Installation\n\n```bash\ngo install github.com/kazhuravlev/options-gen/cmd/options-gen@latest\n```\n\n## Usage\n\n```go\npackage mypkg\n\nimport (\n\t\"io\"\n\t\"log\"\n)\n\n//go:generate options-gen -out-filename=options_generated.go -from-struct=Options\ntype Options struct {\n\tlogger     log.Logger `option:\"mandatory\"`\n\tlistenAddr string     `option:\"mandatory\" validate:\"required,hostname_port\"`\n\tcloser     io.Closer  `validate:\"required\"`\n}\n```\n\n```bash\ngo generate ./...\n```\n\nThis will generate `out-filename` file with options constructor. Like this:\n\n```go\n// options_generated.go\npackage mypkg\n\nimport (\n\t\"log\"\n)\n\nfunc NewOptions(\n\t// mandatory options. you cannot ignore or forget them because they are arguments.\n\tlogger log.Logger,\n\tlistenAddr string,\n\t// optional: you can leave them empty or not.\n\tother ...Option,\n) {\n\t// ...\n}\n\n// Validate will check that all options are in desired state\nfunc (o *Options) Validate() error {\n\t// ...\n}\n```\n\nAnd you can use generated options as follows:\n\n```go\npackage mypkg\n\nimport \"fmt\"\n\ntype Component struct {\n\topts Options // struct that you define as struct with options \n}\n\nfunc New(opts Options) (*Component, error) { // constructor of your service/client/component\n\tif err := opts.Validate(); err != nil {    // always add only these lines for all your constructors\n\t\treturn nil, fmt.Errorf(\"cannot validate options: %w\", err)\n\t}\n\n\treturn \u0026Component{opts: opts}, nil // embed options into your component\n}\n```\n\nAnd after that you can use new constructor in (for ex.) `main.go`:\n\n```go\npackage main\n\nfunc main() {\n\tc, err := mypkg.New(mypkg.NewOptions( /* ... */))\n\tif err != nil {\n\t\tpanic(err)\n\t}\n}\n```\n\n## Usage with Generics\n\n```go\npackage mypkg\n\n//go:generate options-gen -from-struct=Options\ntype Options[T any] struct {\n\taddr string   `option:\"mandatory\" validate:\"required,hostname_port\"`\n\tch   \u003c-chan T `option:\"mandatory\"`\n}\n```\n\nAnd just `go generate ./...`.\n\n## Examples\n\nSee an [examples](./examples) to get real-world examples.\n\n## Configuration\n\nTo configure this tool you should know two things: how to work with cli tool\nand how to define options in your `Options` struct.\n\n### CLI tool\n\nAll the tool needs is the information about source and target files and packages.\nTool can be invoked by `options-gen` (after [Installation](#Installation)) and\nit will have the following arguments:\n\n- `filename` - is a source filename that contains `Options` struct relative\n  to the current dir. For example `./pkg/github-client/options.go`.\n  Default: `$GOFILE` (file where you placed `//go:generate`).\n- `pkg` - name of output filename package. In most cases we can just use\n  the same package as the `filename` file. For example `githubclient`.\n  Default: `$GOPACKAGE`. Package name same as file where you placed `//go:generate`.\n- `from-struct` - name of structure that contains our options. For\n  example `Options`.\n- `out-filename` - specifies an output filename. This filename will be rewritten\n  with options-gen specific content. For\n  example `./pkg/github-client/options_generated.go`.\n\nSee an [Examples](#Examples).\n\n### Option tag\n\nYou can control two important things. The first is about the options constructor\n\n- how `options-gen` will generate `NewOptions` constructor. The second is about\n  how to validate data, that has been passed as value for this field.\n\n#### Control the constructor\n\n`options-gen` can generate a constructor that can receive all option fields as\nseparate arguments. It will force the user to pass each (or someone) option\nfield to the constructor. Like this:\n\n```go\n// Mark Field1 as mandatory\ntype Options struct {\n\tfield1 string `option:\"mandatory\"`\n}\n\n// options-gen will generate constructor like this\nfunc NewOptions(field1 string, otherOptions ...option)...\n```\n\nBut, if we do not want to force the user to pass each argument - we can remove\nthe `option:\"mandatory\"` feature for this field and get something like this:\n\n```go\n// Do not mark Field1 as mandatory\ntype Options struct {\n\tfield1 string\n}\n\n// options-gen will generate constructor like this\nfunc NewOptions(otherOptions ...option)...\n```\n\nSo, this allows setting only those options fields that user is want to set.\n\n#### Validate field data\n\nAfter we define the fields, we want to restrict the values of these fields. To\ndo that we can use a well-known library [validator](https://github.com/go-playground/validator)\n\nJust read the docs for `validator` library and add tag to fields like this:\n\n```go\ntype Options struct {\n\tmaxDbConn int `validate:\"required,min=1,max=16\"`\n}\n```\n\n#### Default values\n\n`options-gen` provide several ways to define defaults for options. You can\nchoose which mechanism you need by providing a flag `-defaults-from`. By\ndefault, this flag is set to `tag=default`.\n\n- `tag[=TagName]`. This mechanism will try to find a tag `TagName` in field\n  tags. By default `TagName` is equal to `default`\n- `var[=VariableName]`. This mechanism will copy variable `VariableName` fields\n  to your `Options` instance. By default `VariableName` is equal\n  to `default\u003cStructName\u003e`. This variable should contain `Options` struct.\n- `func[=FunctionName]`. The same as `var`, but for the function name.\n  Function `FunctionName` will be called once per `NewOptions` constructor. This\n  function should return an `Options` struct.\n- `none` to disable defaults.\n\n##### Using tag\n\nFor numbers, strings, and `time.Duration` you can set the default value:\n\n```go\n// simple example\n//go:generate options-gen -from-struct=Options\ntype Options struct {\n\tpingPeriod  time.Duration `default:\"3s\" validate:\"min=100ms,max=30s\"`\n\tname        string        `default:\"unknown\" validate:\"required\"`\n\tmaxAttempts int           `default:\"10\" validate:\"min=1,max=10\"`\n\teps         float32       `default:\"0.0001\" validate:\"gt=0\"`\n}\n```\n\n```go\n// custom default tag\n//go:generate options-gen -from-struct=Options --default-from=tag=mydefaulttag\ntype Options struct {\n\tpingPeriod  time.Duration `mydefaulttag:\"3s\" validate:\"min=100ms,max=30s\"`\n\tname        string        `mydefaulttag:\"unknown\" validate:\"required\"`\n\tmaxAttempts int           `mydefaulttag:\"10\" validate:\"min=1,max=10\"`\n\teps         float32       `mydefaulttag:\"0.0001\" validate:\"gt=0\"`\n}\n```\n\nIt would be relevant if the field were not filled either explicitly or through\nfunctional option.\n\nThe default value must be valid for the field type and must satisfy validation\nrules.\n\n##### Using variable\n\nTags allow you to define defaults for simple types like `string`, `number`\n, `time.Duration`. When you want to define a variable with prefilled values -\nyou can do this like that:\n\n```go\n// simple example\n//go:generate options-gen -from-struct=Options -defaults-from=var\ntype Options struct {\n\thttpClient *http.Client\n}\n\nvar defaultOptions = Options{\n\thttpClient: \u0026http.Client{},\n}\n```\n\n```go\n// custom variable name\n//go:generate options-gen -from-struct=Options -defaults-from=var=myDefaults\ntype Options struct {\n\thttpClient *http.Client\n}\n\nvar myDefaults = Options{\n\thttpClient: \u0026http.Client{},\n}\n```\n\n##### Using function\n\nThe same as variable. See an examples:\n\n```go\n// simple example\n//go:generate options-gen -from-struct=Options -defaults-from=func\ntype Options struct {\n\thttpClient *http.Client\n}\n\nfunc getDefaultOptions() Options {\n\treturn Options{\n\t\thttpClient: \u0026http.Client{},\n\t}\n}\n```\n\n```go\n// custom function name\n//go:generate options-gen -from-struct=Options -defaults-from=func=myDefaults\ntype Options struct {\n\thttpClient *http.Client\n}\n\nfunc myDefaults() Options {\n\treturn Options{\n\t\thttpClient: \u0026http.Client{},\n\t}\n}\n```\n\n##### Disable defaults\n\nIf you want to be sure that defaults will not be parsed - you can specify\nthe `none` for `-defaults-from` flag.\n\n```go\n// defaults will now be parsed at all\n//go:generate options-gen -from-struct=Options -defaults-from=none\ntype Options struct {\n\tname string `default:\"joe\"`\n}\n```\n\n#### Which fields are set?\n\n`options-gen` can produce additional code that allows you to check which fields were set. To do this, simply add\nthe `-with-isset` flag to `options-gen`.\n\nFor example, this code with the specified option...\n\n```go\npackage app\n\n//go:generate options-gen -from-struct=Options -with-isset\ntype Options struct {\n\tname string\n}\n```\n\n...will produce function `func (o *Options) IsSet(field optField) bool{...}`. \n\n### Custom validator\n\nYou can override `options-gen` validator for specific struct by implementing\nthe `Validator()` method:\n\n```go\nimport \"github.com/mycoolmodule/internal/validator\"\n\n// ...\n\nfunc (Options) Validator() *validator.Validate {\nreturn validator.Validator\n}\n```\n\nOr you can override `options-gen` validator globally:\n\n```go\npackage validator\n\nimport (\n\tgoplvalidator \"github.com/go-playground/validator/v10\"\n\toptsValidator \"github.com/kazhuravlev/options-gen/pkg/validator\"\n)\n\nvar Validator = goplvalidator.New()\n\nfunc init() {\n\tmust(Validator.RegisterValidation( /* ... */))\n\tmust(Validator.RegisterAlias( /* ... */))\n\n\toptsValidator.Set(Validator)\n}\n```\n\n## Contributing\n\nThe development process is pretty simple:\n\n- [Fork](https://docs.github.com/en/get-started/quickstart/fork-a-repo) the repo\n  on GitHub\n- [Clone](https://docs.github.com/en/get-started/quickstart/fork-a-repo#cloning-your-forked-repository)\n  your copy of the repo\n- [Create a new branch](https://git-scm.com/book/en/v2/Git-Branching-Basic-Branching-and-Merging)\n  for your goals\n- Install the [Task](https://taskfile.dev/installation/). It's like `Make`, but\n  simple\n- Check that your working copy is ready to start development by\n  running `task check` in repo workdir\n- Reach your goals!\n- Check that all is ok by `task check`\n- [Create](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request)\n  a Pull Request\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkazhuravlev%2Foptions-gen","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkazhuravlev%2Foptions-gen","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkazhuravlev%2Foptions-gen/lists"}