go-option

module
v1.0.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 7, 2026 License: Apache-2.0

README

go-option

Go 函数选项模式(Functional Options Pattern)代码自动生成工具。

根据结构体定义,自动生成 Option 类型、New 构造函数和 With 选项函数,消除手写样板代码。

支持两种生成风格:接口模式(interface)和闭包模式(closure),完整支持泛型。

本项目基于 go-optioner 改进,新增接口模式、关键字安全、通道方向、联合约束等能力,并修复了多项已知缺陷。


安装

go install github.com/gtkit/go-option/cmd/go-option@latest

验证安装:

go-option

输出:

go-option is a tool for generating functional options pattern.
Usage:
        go-option [flags]
Flags:
        -type <struct name>
        -output <output path>, default: srcDir/opt_xxx_gen.go
        -prefix <With function prefix>, default: With{FieldName}
        -mode <file writing mode>, default: write
                - write: Overwrites or creates a new file.
                - append: Adds to the end of the file.
        -style <code generation style>, default: interface
                - interface: Interface-based options pattern.
                - closure: Closure-based options pattern (go-optioner compatible).

如果提示命令找不到,请确认 $GOPATH/bin 已添加到系统环境变量 PATH 中。


快速开始

1. 定义结构体
package example

//go:generate go-option -type User
type User struct {
    Name   string `opt:"-"` // 必填字段:作为构造函数的必传参数
    Age    int              // 可选字段:生成 WithAge 函数
    Gender string           // 可选字段:生成 WithGender 函数
}

标签规则:

  • opt:"-":标记为必填字段,作为 NewUser() 构造函数的必传参数,不生成 With 函数。
  • 无标签或其他值:标记为可选字段,生成对应的 WithXxx() 函数。
2. 生成代码

方式一:直接执行命令

go-option -type User

方式二:使用 go generate

go generate ./...
3. 生成的代码(interface 模式,默认)
// Generated by [go-option] -type User; DO NOT EDIT

package example

type UserOption interface {
    apply(*User)
}

func NewUser(name string, opts ...UserOption) *User {
    user := &User{
        Name: name,
    }
    for _, opt := range opts {
        opt.apply(user)
    }
    return user
}

type userAgeOpt struct {
    age int
}

func (u userAgeOpt) apply(opt *User) {
    opt.Age = u.age
}

func WithAge(age int) UserOption {
    return userAgeOpt{age: age}
}

// ... WithGender 类似
4. 使用生成的代码
// 只传必填参数
user := NewUser("张三")

// 传必填参数 + 可选参数
user := NewUser("张三", WithAge(25), WithGender("male"))

生成风格

接口模式(默认)
go-option -type User -style interface

每个可选字段生成一个实现了 apply 方法的 unexported 结构体,类型安全,零额外分配。

闭包模式(兼容 go-optioner)
go-option -type User -style closure

生成的代码:

type UserOption func(*User)

func NewUser(name string, opts ...UserOption) *User {
    user := &User{Name: name}
    for _, opt := range opts {
        opt(user)
    }
    return user
}

func WithAge(age int) UserOption {
    return func(user *User) {
        user.Age = age
    }
}

闭包模式更简洁,Option 类型为 func(*T),每个 With 函数返回一个闭包。


命令行参数

参数 类型 必填 默认值 说明
-type string - 目标结构体名称
-output string opt_<snake>_gen.go 输出文件路径
-prefix string With 函数的自定义前缀
-with_prefix string -prefix(兼容 go-optioner)
-mode enum write write(覆盖)或 append(追加)
-style enum interface interface(接口)或 closure(闭包)

进阶用法

自定义 With 函数前缀

当同一个包中有多个结构体需要生成选项,为避免 WithName 等函数名冲突:

//go:generate go-option -type User -prefix User
//go:generate go-option -type Server -prefix Server

生成 WithUserNameWithServerName 而非都叫 WithName

自定义输出路径
//go:generate go-option -type User -output ./options/user_option.go
追加模式(多结构体共用一个文件)
//go:generate go-option -type User -mode write -output ./options.go
//go:generate go-option -type Server -mode append -output ./options.go

第一个结构体使用 write 创建文件,后续使用 append 追加到同一文件。

泛型结构体
//go:generate go-option -type Container -style closure
type Container[T any, U comparable] struct {
    ID    string `opt:"-"`
    Value T
    Index U
}

生成的代码完整保留泛型参数:

type ContainerOption[T any, U comparable] func(*Container[T, U])

func NewContainer[T any, U comparable](id string, opts ...ContainerOption[T, U]) *Container[T, U] {
    // ...
}

func WithValue[T any, U comparable](value T) ContainerOption[T, U] {
    // ...
}
联合类型约束
type Number interface {
    ~int | ~float64
}

//go:generate go-option -type Calc
type Calc[T Number] struct {
    Name  string `opt:"-"`
    Value T
}
嵌入结构体
type Base struct {
    ID int
}

//go:generate go-option -type Service
type Service struct {
    Base               // 可选嵌入字段,生成 WithBase
    Name string `opt:"-"` // 必填
    Port int            // 可选
}
跨包类型字段
import "crypto/tls"

//go:generate go-option -type Server
type Server struct {
    Addr      string      `opt:"-"`
    TLSConfig *tls.Config  // 自动处理跨包导入
}

生成的文件会自动添加 import "crypto/tls" 导入语句。


go:generate 批量示例

package example

import "time"

//go:generate go-option -type RedisClient -prefix Redis
type RedisClient struct {
    Addr         string        `opt:"-"` // 必填:Redis 地址
    Password     string                   // 可选:密码
    DB           int                      // 可选:数据库编号
    DialTimeout  time.Duration            // 可选:连接超时
    ReadTimeout  time.Duration            // 可选:读超时
    WriteTimeout time.Duration            // 可选:写超时
    PoolSize     int                      // 可选:连接池大小
}

执行 go generate ./... 后生成:

  • NewRedisClient(addr string, opts ...RedisClientOption) *RedisClient
  • WithRedisPassword(password string) RedisClientOption
  • WithRedisDB(db int) RedisClientOption
  • WithRedisDialTimeout(dialTimeout time.Duration) RedisClientOption
  • ...

支持的字段类型

类型 示例 支持
基本类型 string, int, bool
指针 *string, *Config
切片 []int, []string
数组 [3]byte, [10]int
Map map[string]int
通道 chan int, chan<- int, <-chan int
函数 func(), func(int) error
接口 interface{}, io.Reader
结构体 struct{}
跨包类型 tls.Config, time.Duration
嵌入结构体 Embedded, *Embedded
泛型实例化 Pair[string, int]
可变参数 func(...int)
联合约束 ~int | ~float64

安全特性

  • 关键字安全:字段名转换后若为 Go 关键字(如 Funcfunc),自动追加 _ 后缀避免编译错误。
  • 类型名防冲突:interface 模式的内部 option 结构体使用 unexported 命名(如 userAgeOpt),不会与包中已有类型冲突。
  • 闭包变量防冲突:closure 模式使用完整结构体名(如 user)而非单字母作为闭包参数,避免与单字母字段名冲突。
  • 导入自动管理:使用 goimports 自动添加/移除导入语句,支持跨包类型。

项目结构

go-option/
├── cmd/go-option/main.go          # CLI 入口
├── options/
│   ├── options_generator.go       # 核心代码生成逻辑
│   ├── options_generator_test.go  # 全面测试(含编译验证)
│   ├── tool.go                    # 字符串工具函数
│   ├── tool_test.go               # 工具函数测试
│   └── testdata/                  # 测试用例数据
├── templates/
│   ├── tmpl.go                    # 模板 embed 声明
│   └── tpl/                       # 模板文件
│       ├── option.tmpl            # 接口模式(write)
│       ├── option_closure.tmpl    # 闭包模式(write)
│       ├── additional.tmpl        # 接口模式(append)
│       ├── additional_closure.tmpl # 闭包模式(append)
│       └── header.tmpl            # 文件头注释
├── example/                       # 使用示例
├── test/                          # AST 解析测试
├── go.mod
├── Makefile
└── .golangci.yml

License

Apache License 2.0

Directories

Path Synopsis
cmd
go-option command
Package options 提供函数选项模式(Functional Options Pattern)的代码生成核心逻辑。
Package options 提供函数选项模式(Functional Options Pattern)的代码生成核心逻辑。

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL