go-options-gen

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

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 in
      options_generated.go
      .
    • Multiple Option Sets: For a component named
      MyService
      , the struct (e.g.,
      MyServiceOptions
      ) MUST be in
      myservice_options.go
      , and generated code MUST be in
      myservice_options_generated.go
      .
  • 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.mod
      using:
      bash
      go get -tool github.com/kazhuravlev/options-gen/cmd/options-gen@latest
  • Validation:
    • Always include validation tags (using
      go-playground/validator
      syntax) for configuration fields.
    • ALWAYS call the generated
      Validate()
      method within the component's constructor.
  • Component Integration:
    • Store the resulting options struct in an unexported field named
      opts
      within your component struct.
  • 文件命名
    • 单个选项集:结构体定义必须放在
      options.go
      中,生成的代码必须放在
      options_generated.go
      中。
    • 多个选项集:对于名为
      MyService
      的组件,结构体(如
      MyServiceOptions
      )必须放在
      myservice_options.go
      中,生成的代码必须放在
      myservice_options_generated.go
      中。
  • 封装性
    • 结构体中的选项字段应设为未导出(以小写字母开头),防止从包外直接修改。
  • 工具使用
    • 始终使用
      go tool options-gen
      运行该工具。
    • 使用以下命令在
      go.mod
      中安装并追踪该工具:
      bash
      go get -tool github.com/kazhuravlev/options-gen/cmd/options-gen@latest
  • 验证
    • 始终为配置字段包含验证标签(使用
      go-playground/validator
      语法)。
    • 必须在组件的构造函数中调用生成的
      Validate()
      方法。
  • 组件集成
    • 将最终的选项结构体存储在组件结构体中名为
      opts
      的未导出字段中。

Developer Workflow

开发者工作流

  1. Installation: Ensure the tool is tracked in your project:
    bash
    go get -tool github.com/kazhuravlev/options-gen/cmd/options-gen@latest
  2. Define Options (
    options.go
    )
    : Define your options struct with unexported fields. Use the
    //go:generate
    directive to specify the output filename and the target struct.
    go
    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"`
    }
  3. Generate: Run the generator:
    bash
    go generate ./options.go
  4. Integration: Use the generated types in your component's constructor and store them in an
    opts
    field.
    go
    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
    }
  1. 安装: 确保工具已在项目中被追踪:
    bash
    go get -tool github.com/kazhuravlev/options-gen/cmd/options-gen@latest
  2. 定义选项(
    options.go
    : 定义带有未导出字段的选项结构体。使用
    //go:generate
    指令指定输出文件名和目标结构体。
    go
    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"`
    }
  3. 生成代码: 运行生成器:
    bash
    go generate ./options.go
  4. 集成组件: 在组件的构造函数中使用生成的类型,并将其存储在
    opts
    字段中。
    go
    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 in
    NewOptions()
    .
  • Use
    default:"value"
    for sensible defaults.
    options-gen
    supports basic types and
    time.Duration
    .
  • 对于没有安全默认值的字段(如API密钥、目标URL),使用
    option:"mandatory"
    。这些字段会成为
    NewOptions()
    中的必填参数。
  • 对于合理的默认值,使用
    default:"value"
    options-gen
    支持基础类型和
    time.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
//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"},
    }
}
对于复杂类型(如映射或嵌套结构体),在生成指令中使用
-defaults-from=func
并定义一个提供者函数:
go
//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.
  • 对于任何不能为零值的字段,使用
    validate:"required"
  • 对于枚举类字符串字段,使用
    validate:"oneof=tcp udp"
  • 对于计数器或大小字段,使用
    validate:"min=1"

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"})
).
对于切片字段,使用
option:"variadic=true"
来生成接受多个参数的设置器(如
WithEndpoints("a", "b")
),而不是接受单个切片的设置器(如
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.
通过在
options.go
中保持字段未导出,您可以确保配置组件的唯一方式是通过生成的
With*
设置器,这些设置器可以包含验证逻辑。

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.
  1. Filenames: Use
    <prefix>_options.go
    and
    <prefix>_options_generated.go
    .
  2. Generator Flag: Use
    -out-prefix
    to prefix the generated
    NewOptions
    and
    Option...Setter
    types.
Example for
MyService
(
myservice_options.go
):
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.
当一个包包含多个组件(如
Client
Server
)时,使用前缀避免生成的类型和函数出现命名冲突。
  1. 文件名:使用
    <prefix>_options.go
    <prefix>_options_generated.go
  2. 生成器标志:使用
    -out-prefix
    为生成的
    NewOptions
    Option...Setter
    类型添加前缀。
MyService
的示例(
myservice_options.go
):
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"`
}
这将生成
NewMyServiceOptions
OptionMyServiceOptionsSetter
,使其能够与同一包中的其他选项共存。

Resources

资源

  • Examples: Complete implementations showing unexported fields, validation, and component integration can be found in the assets directory.
  • 示例:展示未导出字段、验证和组件集成的完整实现可在assets目录中找到。