go-options-gen
go-options-gen
You are an expert in using the options-gen library (https://github.com/kazhuravlev/options-gen) to create robust, type-safe functional options for Go components. You prioritize unexported option fields to maintain encapsulation while providing a clean, exported API for configuration.
Core Mandates
- File Naming:
- Single Option Set: Struct definition MUST be in
options.go, and generated code MUST be inoptions_generated.go. - Multiple Option Sets: For a component named
MyService, the struct (e.g.,MyServiceOptions) MUST be inmyservice_options.go, and generated code MUST be inmyservice_options_generated.go.
- Single Option Set: Struct definition MUST be in
- Encapsulation:
- Options fields within the struct SHOULD be unexported (start with a lowercase letter) to prevent direct modification from outside the package.
- Tooling:
- Always run the tool using
go tool options-gen. - Install and track the tool in
go.modusing:go get -tool github.com/kazhuravlev/options-gen/cmd/options-gen@latest
- Always run the tool using
- Validation:
- Always include validation tags (using
go-playground/validatorsyntax) for configuration fields. - ALWAYS call the generated
Validate()method within the component's constructor.
- Always include validation tags (using
- Component Integration:
- Store the resulting options struct in an unexported field named
optswithin your component struct.
- Store the resulting options struct in an unexported field named
Developer Workflow
-
Installation: Ensure the tool is tracked in your project:
go get -tool github.com/kazhuravlev/options-gen/cmd/options-gen@latest -
Define Options (
options.go): Define your options struct with unexported fields. Use the//go:generatedirective to specify the output filename and the target struct.package mypackage import "time" //go:generate go tool options-gen -from-struct=Options -out-filename=options_generated.go type Options struct { timeout time.Duration `option:"mandatory" validate:"required"` maxRetries int `default:"3" validate:"min=1"` endpoints []string `option:"variadic=true"` } -
Generate: Run the generator:
go generate ./options.go -
Integration: Use the generated types in your component's constructor and store them in an
optsfield.type Component struct { opts Options } func New(setters ...OptionOptionsSetter) (*Component, error) { opts := NewOptions(setters...) if err := opts.Validate(); err != nil { return nil, fmt.Errorf("invalid options: %w", err) } return &Component{opts: opts}, nil }
Expert Guidance
Mandatory vs. Default
- Use
option:"mandatory"for fields that have no safe default (e.g., API keys, target URLs). These become required arguments inNewOptions(). - Use
default:"value"for sensible defaults.options-gensupports basic types andtime.Duration.
Advanced Defaults
For complex types (like maps or nested structs), use -defaults-from=func in the generate directive and define a provider function:
//go:generate go tool options-gen -from-struct=Options -out-filename=options_generated.go -defaults-from=func
func defaultOptions() Options {
return Options{
headers: map[string]string{"User-Agent": "my-client"},
}
}
Validation Best Practices
- Use
validate:"required"for any field that must not be zero-valued. - Use
validate:"oneof=tcp udp"for enum-like string fields. - Use
validate:"min=1"for counters or sizes.
Variadic Setters
For slice fields, use option:"variadic=true" to generate a setter that accepts multiple arguments (e.g., WithEndpoints("a", "b")) instead of a single slice (e.g., WithEndpoints([]string{"a", "b"})).
Avoiding Exported Fields
By keeping fields unexported in options.go, you ensure that the only way to configure the component is through the generated With* setters, which can include validation logic.
Multiple Options in One Package
When a package contains multiple components (e.g., Client and Server), use prefixes to avoid name collisions in generated types and functions.
- Filenames: Use
<prefix>_options.goand<prefix>_options_generated.go. - Generator Flag: Use
-out-prefixto prefix the generatedNewOptionsandOption...Settertypes.
Example for MyService (myservice_options.go):
//go:generate go tool options-gen -from-struct=MyServiceOptions -out-filename=myservice_options_generated.go -out-prefix=MyService
type MyServiceOptions struct {
timeout time.Duration `option:"mandatory"`
}
This will generate NewMyServiceOptions and OptionMyServiceOptionsSetter, allowing them to coexist with other options in the same package.
Resources
- Examples: Complete implementations showing unexported fields, validation, and component integration can be found in the assets directory.
More from metalagman/agent-skills
go-goose
Use this skill to plan, write, or run database migrations with the pressly/goose CLI and Go library (SQL/Go migrations, env vars, provider API, embedded migrations).
27beads
Use this skill to manage work in Beads (`bd`) 1.0+, a Dolt-backed issue tracker for AI agents, including issue lifecycle, agent setup, recovery, workflows, and multi-repo coordination.
18github-flow
Use this skill when working with the lightweight GitHub Flow branching model. Ideal for projects with continuous deployment where 'main' is always deployable.
17go-senior-developer
Expert senior-level Go guidance for architecture, API-first design/codegen, advanced concurrency, performance tuning, testing/quality, cloud-native 12-factor practices, and Go 1.24+ tooling for large-scale systems.
17go-google-style-decisions
Expertise in Go programming decisions according to the Google Go Style Guide. Focuses on specific choices for naming, error handling, and language usage.
17go-google-style-guide
Expertise in Go programming according to the Google Go Style Guide. Use when the user needs to write, refactor, or review Go code for clarity, simplicity, and maintainability. This skill ensures adherence to Google's official Go idioms, formatting, and the "Least Mechanism" principle.
16