optiongen

package module
v1.0.8 Latest Latest
Warning

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

Go to latest
Published: Apr 29, 2022 License: BSD-3-Clause Imports: 24 Imported by: 0

README

optiongen

GoDoc Go Report CardSourcegraph

optiongen is a fork of XSAM/optionGen, a tool to generate go Struct option for test, mock or more flexible. The purpose of this fork is to provide more powerful and flexible option generation.

Functional Options

Functional options are an idiomatic way of creating APIs with options on types. The initial idea for this design pattern can be found in an article published by Rob Pike called Self-referential functions and the design of options.

Install

Install using go install, and this will build the optionGen binary in $GOPATH/bin.

go install github.com/timestee/optiongen/cmd/optiongen@latest

optionGen require goimports to format code which is generated. So you may confirm that goimports has been installed

go install golang.org/x/tools/cmd/goimports@latest

快速开始

type WatchError = func(loaderName string, confPath string, watchErr error)

//go:generate optiongen --option_with_struct_name=true --xconf=true --usage_tag_name=usage --xconf=true
func RedisOptionDeclareWithDefault() interface{} {
	return map[string]interface{}{
		"Endpoints":      []string{"192.168.0.1", "192.168.0.2"},
		"Cluster":        true,
		"TimeoutsStruct": (Timeouts)(Timeouts{}),
	}
}
//go:generate optiongen
func XXXXXXOptionDeclareWithDefault() interface{} {
	return map[string]interface{}{
		"Endpoints":        []string{"10.0.0.1", "10.0.0.2"},
		"ReadTimeout":      time.Duration(time.Second),
		"TypeMapIntString": map[int]string{1: "a", 2: "b"},
		"TypeSliceInt64":   []int64{1, 2, 3, 4},
		"TypeBool":         false,
		"MapRedis":         (map[string]*Redis)(map[string]*Redis{"test": NewRedis()}),
		// annotation@Redis(getter="RedisVisitor")
		"Redis":              (*Redis)(NewRedis()), // 辅助指定类型为*Redis
		"OnWatchError":       WatchError(nil),      // 辅助指定类型为WatchError
		"OnWatchErrorNotNil": func(loaderName string, confPath string, watchErr error) {},
		"TypeSliceDuratuon":  []time.Duration([]time.Duration{time.Second, time.Minute, time.Hour}), // 辅助指定类型为WatchError
	}
}
  • 命令提示://go:generate optiongen
  • 声明XXXOptionDeclareWithDefault, optiongen会识别后缀为OptionDeclareWithDefault的func声明做进一步处理,XXXOptionDeclareWithDefault的函数约定如上代码段所示。
  • 在XXXOptionDeclareWithDefault函数返回的 map[string]interface{}中声明字段名称,默认值
    • 基础类型如上例中的Endpoints,TypeBool等基础类型或者基础类型的slice,map等可以直接以字面值给出默认值
    • 函数类型如果非空可以直接以字面值给出默认值,如OnWatchErrorNotNil,但OnWatchError的默认值为nil,则需要辅助性的给出类型定义WatchError
    • time.Duration,[]time.Duration,map[string]*Redis此类的非基础类型的slice或者map都需要辅助指明类型

    在使用过程中可以尝试运行,如optiongen遇到无法处理的类型会给出错误提示,如将TypeSliceDuratuon默认值写为[]time.Duration{time.Second, time.Minute, time.Hour}会得到如下错误提示: panic: optionGen "TypeSliceDuratuon" got type []time.Duration support basic types only

  • 运行go generate,会有如下输出
    🚀  optiongen running => /xxxxxx/github/optiongen/example/config.go:159 [XXXXXXOptionDeclareWithDefault] ...
    

使用帮助

optiongen支持的参数

可以在//go:generate optiongen中根据具体需求加入参数调整代码生成行为,支持的参数可以运行:optiongen --help查看。以上文提到的XXXXXXOptionDeclareWithDefault为例。

  • --debug, bool类型,默认false,是否打开调试模式,调试模式下会输出详尽的运行日志,主要用于开发调试

  • --new_func,生成的Struct的New方法名称,如不指定则默认为默认为NewXXXXXX,可以指定为如NewConf

  • --new_func_return,生成的Struct的New方法的返回类型,默认 pointer

    • pointer, 返回类型指针: *XXXXXX,比如定义类型为confOptionDeclareWithDefault首字符小写,生成的配置为conf不会被导出,此时将返回类型设定为interfacevisitor更为恰当。
    • interface,返回类型接口: XXXXXXInterface
    • visitor,返回类型访问接口: XXXXXXVisitor
  • --option_prefix,生成的Option方法前缀

    • 默认设置下,上例中的Endpoints字段生成的Option方法签名为WithEndpoints
    • 为了避免方法签名冲突,也更为明确方法的含义(一个package中定义了多个Option结构),可以指定Option方法前缀如:WithServer,则生成的Option方法签名为WithServerEndpoints
  • --option_with_struct_name,默认false,功能与--option_prefix类似,Option名称是否携带Struct名称

    • 如设定为true,在不设定--option_prefix的情况下,上例中的Endpoints生成的Option签名为:WithXXXXXXEndpoints
    • 设定--option_prefix时,该参数无效
  • --option_return_previous, bool类型,默认true,生成的Option方法是否返回原始值 返回原始值的Option签名为:

    // WithReadTimeout option func for ReadTimeout
    func WithReadTimeout(v time.Duration) XXXXXXOption {
    	return func(cc *XXXXXX) XXXXXXOption {
    		previous := cc.ReadTimeout
    		cc.ReadTimeout = v
    		return WithReadTimeout(previous)
    	}
    }
    // 可以在一些测试场景下将参数设定为需要的值,在退出当前场景后将配置恢复
    func TestApplyOption(xx XXXXXXInterface) {
    	old := xx.ApplyOption(WithReadTimeout(time.Second))
    	defer xx.ApplyOption(old...)
    	// ...
    }
    

    不返回原始值的Option签名:

    // WithReadTimeout option func for ReadTimeout
    func WithReadTimeout(v time.Duration) XXXXXXOption {
    	return func(cc *XXXXXX)  {
    		cc.ReadTimeout = v
    	}
    }
    
  • --xconf,bool类型,默认false,是否生成XConf支持

    • 如设定为true,会生成XConf所需的TAG,更新逻辑等,此时可以将optiongen作为配置存在
    • 如有配置热更需求如对接ETCD,Apollo,文件系统等,XConf解析时传入AtomicETCD,XConf会自动解析配置,自动在配置更新后将最新的配置更新到AtomicETCD返回的指针中。
    //go:generate optiongen --option_prefix=WithETCD --xconf=true  --usage_tag_name=usage --option_return_previous=false
    func ETCDOptionDeclareWithDefault() interface{} {
    	return map[string]interface{}{
    		// annotation@Endpoints(comment="etcd地址")
    		"Endpoints": []string{"10.0.0.1", "10.0.0.2"},
    		// annotation@TimeoutsPointer(comment="timeout设置")
    		"TimeoutsPointer": (*Timeouts)(&Timeouts{}),
    		// annotation@writeTimeout(private="true",arg=1)
    		"writeTimeout": time.Duration(time.Second),
    		// annotation@Redis(getter="RedisVisitor")
    		"Redis": (*Redis)(NewRedis()),
    }
    // ETCD should use NewETCD to initialize it
    type ETCD struct {
    	// annotation@Endpoints(comment="etcd地址")
    	Endpoints []string `xconf:"endpoints" usage:"etcd地址"`
    	// annotation@TimeoutsPointer(comment="timeout设置")
    	TimeoutsPointer *Timeouts `xconf:"timeouts_pointer" usage:"timeout设置"`
    }
    // AtomicSetFunc used for XConf
    func (cc *ETCD) AtomicSetFunc() func(interface{}) { return AtomicETCDSet }
    // InstallCallbackOnAtomicETCDSet install callback
    func InstallCallbackOnAtomicETCDSet(callback func(cc ETCDInterface) bool) { ... }
    // AtomicETCDSet atomic setter for *ETCD
    func AtomicETCDSet(update interface{}) {...}
    // AtomicETCD return atomic *ETCDVisitor
    func AtomicETCD() ETCDVisitor {...}
    
  • --usage_tag_name,字符串类型,生成的usage标签名称,默认空,如指定:usage会生生成usage信息,XConf会将这部分信息展现在xonf.Usage以及FlagSet.Usage中。

optiongen支持的标注

optiongen支持通过标注的方式对字段级的代码生成进行更为灵活的控制,目前支持通过在注释中定位标注格式如下:

// annotation@Redis(getter="RedisVisitor") 
// annotation@ReadTimeout(private="true", xconf="read_timeout_user_define_name")
// annotation@TypeMapStringIntNotLeaf(xconf="type_map_string_int_not_leaf,notleaf")
// annotation@ReadTimeout(arg=1,tag_json=",omitempty")
  • private,指定字段为私有字段,不生成Option,不会影响字段本身的访问属性,字段本身的访问属性设定通过首字符大小写决定,如上例ETCDOptionDeclareWithDefaultwriteTimeout字段。
  • arg,指定arg参数的字段不会生成Option方法,并会作为New方法的参数存在
    • 如上例指定writeTimeout的arg,则生成的New方法为:NewETCD(writeTimeout time.Duration, opts ...ETCDOption)
    • 允许设定多个arg,指定参数的index即可,index不可重复
  • xconf,自定义xconf标签
  • inline,将字段inline
  • getter,生成的Get接口返回值类型,默认为定义时指定的类型,可通过该方式指定返回类型对应的接口,如上例中Redis的访问接口返回为:RedisVisitor
  • option, 指定该字段生成的option方法名称,覆盖--option_prefix--option_with_struct_name规则,
  • deprecated,字符串,指定字段为deprecated,在Option以及Get方法上都会生成//Deprecated注释,如果启用了xconf支持,会一并在xconf标签中生成deprecated支持。
  • tag_{name},其中{name}为tag名称,如json,例如tag_json=",omitempty"

Documentation

Index

Constants

View Source
const (
	AnnotationKeyComment    = "comment"
	AnnotationKeyPrivate    = "private"
	AnnotationKeyArg        = "arg"
	AnnotationKeyXConfTag   = "xconf"
	AnnotationKeyGetter     = "getter"
	AnnotationKeyOption     = "option"
	AnnotationKeyVisit      = "visit"
	AnnotationKeyInline     = "inline"
	AnnotationKeyDeprecated = "deprecated"
	AnnotationKeyTag        = "tag_"
)
View Source
const NewFuncReturnInterface = "interface"
View Source
const NewFuncReturnPointer = "pointer"
View Source
const NewFuncReturnVisitor = "visitor"
View Source
const (
	OptionGen = "optiongen"
)

Variables

This section is empty.

Functions

func AtomicConfigSet

func AtomicConfigSet(update interface{})

AtomicConfigSet atomic setter for *Config

func ConfigOptionDeclareWithDefault

func ConfigOptionDeclareWithDefault() interface{}

func InstallCallbackOnAtomicConfigSet added in v1.0.1

func InstallCallbackOnAtomicConfigSet(callback func(cc ConfigInterface) bool)

InstallCallbackOnAtomicConfigSet install callback

func InstallConfigWatchDog

func InstallConfigWatchDog(dog func(cc *Config))

InstallConfigWatchDog the installed func will called when NewTestConfig called

func ParseDir

func ParseDir(dir string)

Types

type BufWrite

type BufWrite struct {
	// contains filtered or unexported fields
}

type Config

type Config struct {
	OptionPrefix         string `xconf:"option_prefix" usage:"option func name prefix, like: With, WithRedis"`                             // annotation@OptionPrefix(comment="option func name prefix, like: With, WithRedis")
	OptionWithStructName bool   `xconf:"option_with_struct_name" usage:"should the option func with struct name?"`                         // annotation@OptionWithStructName(comment="should the option func with struct name?")
	OptionReturnPrevious bool   `xconf:"option_return_previous" usage:"should option func return the previous ones?"`                      // annotation@OptionReturnPrevious(comment="should option func return the previous ones?")
	NewFunc              string `xconf:"new_func" usage:"new function name"`                                                               // annotation@NewFunc(comment="new function name")
	NewFuncReturn        string `xconf:"new_func_return" usage:"valid data: pointer,interface,visitor"`                                    // annotation@NewFuncReturn(comment="valid data: pointer,interface,visitor")
	Verbose              bool   `xconf:"v,deprecated" usage:"Deprecated: use --debug instead"`                                             // annotation@Verbose(xconf="v",deprecated="use --debug instead")
	UsageTagName         string `xconf:"usage_tag_name" usage:"usage tag name,if not empty,will gen usage support for xconf/xflag"`        // annotation@UsageTagName(comment="usage tag name,if not empty,will gen usage support for xconf/xflag")
	EmptyCompositeNil    bool   `xconf:"empty_composite_nil" usage:"should empty slice or map to be nil? otherwise will be make(XXXX,0)"`  // annotation@EmptyCompositeNil(comment="should empty slice or map to be nil? otherwise will be make(XXXX,0)")
	Debug                bool   `xconf:"debug" usage:"debug will print more detail info"`                                                  // annotation@Debug(comment="debug will print more detail info")
	XConf                bool   `xconf:"xconf" usage:"should gen xconf tag support?"`                                                      // annotation@XConf(xconf="xconf",comment="should gen xconf tag support?")
	XConfTrimPrefix      string `xconf:"x_conf_trim_prefix" usage:"if enable xconf tag, the tag value will trim prefix [XConfTrimPrefix]"` // annotation@XConfTrimPrefix(comment="if enable xconf tag, the tag value will trim prefix [XConfTrimPrefix]")
}

Config should use NewTestConfig to initialize it

func NewTestConfig

func NewTestConfig(opts ...ConfigOption) *Config

NewTestConfig new Config

func (*Config) ApplyOption

func (cc *Config) ApplyOption(opts ...ConfigOption) []ConfigOption

ApplyOption apply mutiple new option and return the old ones sample: old := cc.ApplyOption(WithTimeout(time.Second)) defer cc.ApplyOption(old...)

func (*Config) AtomicSetFunc

func (cc *Config) AtomicSetFunc() func(interface{})

AtomicSetFunc used for XConf

func (*Config) GetDebug

func (cc *Config) GetDebug() bool

func (*Config) GetEmptyCompositeNil

func (cc *Config) GetEmptyCompositeNil() bool

func (*Config) GetNewFunc

func (cc *Config) GetNewFunc() string

func (*Config) GetNewFuncReturn added in v1.0.1

func (cc *Config) GetNewFuncReturn() string

func (*Config) GetOptionPrefix

func (cc *Config) GetOptionPrefix() string

all getter func

func (*Config) GetOptionReturnPrevious

func (cc *Config) GetOptionReturnPrevious() bool

func (*Config) GetOptionWithStructName

func (cc *Config) GetOptionWithStructName() bool

func (*Config) GetUsageTagName

func (cc *Config) GetUsageTagName() string

func (*Config) GetVerbose deprecated

func (cc *Config) GetVerbose() bool

GetVerbose visitor func for filed Verbose

Deprecated: use --debug instead

func (*Config) GetXConf

func (cc *Config) GetXConf() bool

func (*Config) GetXConfTrimPrefix

func (cc *Config) GetXConfTrimPrefix() string

type ConfigInterface

type ConfigInterface interface {
	ConfigVisitor
	ApplyOption(...ConfigOption) []ConfigOption
}

ConfigInterface visitor + ApplyOption interface for Config

type ConfigOption

type ConfigOption func(cc *Config) ConfigOption

ConfigOption option func

func WithDebug

func WithDebug(v bool) ConfigOption

WithDebug debug will print more detail info

func WithEmptyCompositeNil

func WithEmptyCompositeNil(v bool) ConfigOption

WithEmptyCompositeNil should empty slice or map to be nil? otherwise will be make(XXXX,0)

func WithNewFunc

func WithNewFunc(v string) ConfigOption

WithNewFunc new function name

func WithNewFuncReturn added in v1.0.1

func WithNewFuncReturn(v string) ConfigOption

WithNewFuncReturn valid data: pointer,interface,visitor

func WithOptionPrefix

func WithOptionPrefix(v string) ConfigOption

WithOptionPrefix option func name prefix, like: With, WithRedis

func WithOptionReturnPrevious

func WithOptionReturnPrevious(v bool) ConfigOption

WithOptionReturnPrevious should option func return the previous ones?

func WithOptionWithStructName

func WithOptionWithStructName(v bool) ConfigOption

WithOptionWithStructName should the option func with struct name?

func WithUsageTagName

func WithUsageTagName(v string) ConfigOption

WithUsageTagName usage tag name,if not empty,will gen usage support for xconf/xflag

func WithVerbose deprecated

func WithVerbose(v bool) ConfigOption

WithVerbose option func for filed Verbose

Deprecated: use --debug instead

func WithXConf

func WithXConf(v bool) ConfigOption

WithXConf should gen xconf tag support?

func WithXConfTrimPrefix

func WithXConfTrimPrefix(v string) ConfigOption

WithXConfTrimPrefix if enable xconf tag, the tag value will trim prefix [XConfTrimPrefix]

type ConfigVisitor

type ConfigVisitor interface {
	GetOptionPrefix() string
	GetOptionWithStructName() bool
	GetOptionReturnPrevious() bool
	GetNewFunc() string
	GetNewFuncReturn() string
	// GetVerbose visitor func for filed Verbose
	//
	// Deprecated: use --debug instead
	GetVerbose() bool
	GetUsageTagName() string
	GetEmptyCompositeNil() bool
	GetDebug() bool
	GetXConf() bool
	GetXConfTrimPrefix() string
}

ConfigVisitor visitor interface for Config

func AtomicConfig

func AtomicConfig() ConfigVisitor

AtomicConfig return atomic *ConfigVisitor

type FieldType

type FieldType int
const (
	FieldTypeFunc FieldType = iota
	FieldTypeVar
)

Directories

Path Synopsis
cmd

Jump to

Keyboard shortcuts

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