diff --git a/README.md b/README.md index 07275d5..70d27a6 100644 --- a/README.md +++ b/README.md @@ -3,3 +3,40 @@ [![Go Reference](https://pkg.go.dev/badge/github.com/goinsane/flagbind.svg)](https://pkg.go.dev/github.com/goinsane/flagbind) Package flagbind provides utilities to bind flags of GoLang's flag package to struct. + +`flagbind` supports these types: + +- bool, *bool +- int, *int +- uint, *uint +- int64, *int64 +- uint64, *uint64 +- int32, *int32 +- uint32, *uint32 +- string, *string +- float64, *float64 +- float32, *float32 +- time.Duration, *time.Duration +- flag.Value +- func(value string) error +- func(name string, value string) error + +If exported known struct field has different type from given types, `Bind` method will panic. +If struct field is unexported or anonymous, struct field will be ignored. + +## Field tags + +### name + +The name tag defines flag name if it is given. Otherwise flag name will be generated by the example: MyFlag to -my-flag. +If the name tag is `-`, struct field will be ignored. + +### usage + +The usage tag defines flag usage by flag package. + +### default + +The default tag defines default flag value by flag package. The default tag gets string value. +When the default tag isn't given; if the struct field is pointer, default value is nil. +Otherwise default value is initial value of struct field. diff --git a/errors.go b/errors.go index bb0552e..c6ea2dd 100644 --- a/errors.go +++ b/errors.go @@ -26,6 +26,3 @@ func numError(err error) error { } return err } - -// errUnknownType is internal use only. -var errUnknownType = errors.New("unknown type") diff --git a/examples/example1.go b/examples/example1/main.go similarity index 67% rename from examples/example1.go rename to examples/example1/main.go index 1aaccc1..38e368a 100644 --- a/examples/example1.go +++ b/examples/example1/main.go @@ -1,6 +1,3 @@ -//go:build ignore -// +build ignore - package main import ( @@ -20,9 +17,18 @@ func main() { StrFlag string `name:"str" default:"abc" usage:"str usage"` CustomFlag float64 `name:"cust" usage:"custom flag usage"` IgnoredFlag int64 `name:"-"` + FuncFlag func(string) error + DefFlag int `default:"35"` + Def2Flag int + } + st.FuncFlag = func(value string) error { + fmt.Println(value) + return nil } + st.DefFlag = 45 + st.Def2Flag = 55 flagbind.Bind(fs, &st) - args := []string{"-bool-flag", "-bool-flag2", "true", "-int-flag", "10", "-str", "def", "-cust", "10.6"} + args := []string{"-bool-flag", "-bool-flag2", "true", "-int-flag", "10", "-str", "def", "-cust", "10.6", "-func-flag", "aaa"} _ = fs.Parse(args) fmt.Printf("%+v\n", st) } diff --git a/flagbind.go b/flagbind.go index 9594450..922f22d 100644 --- a/flagbind.go +++ b/flagbind.go @@ -13,7 +13,7 @@ import ( // Bind binds variables of the given flag.FlagSet to the target that is struct pointer. // Fields of target can have tags such as name, default, usage. // If name tag of struct field is not given, flag name will be generated by struct field name (ex: BoolFlag is -bool-flag). -// If name tag is "-" or struct field isn't exported or anonymous, struct field will be ignored. +// If name tag is "-" or struct field is unexported or anonymous, struct field will be ignored. // It will panic when target isn't struct pointer or nil, or any struct field has unknown type. func Bind(fs *flag.FlagSet, target interface{}) { val := reflect.ValueOf(target) @@ -49,17 +49,12 @@ func Bind(fs *flag.FlagSet, target interface{}) { fs.BoolVar(sVal.Addr().Interface().(*bool), parser.Name, false, parser.Usage) continue } - if e := parser.Set(""); e == errUnknownType { - panic(fmt.Errorf("unknown type for flag -%s", parser.Name)) - } + parser.Reset() parser.SetDefault() fs.Func(parser.Name, parser.Usage, parser.Set) } } -// Func is a type to use in struct as free form data types. -type Func func(name string, value string) error - type _Parser struct { Target reflect.Value Name string @@ -203,22 +198,88 @@ func (p *_Parser) Set(value string) (err error) { if err != nil { return err } - case Func: - err = ifc.(Func)(p.Name, value) + case func(string, string) error: + err = ifc.(func(string, string) error)(p.Name, value) if err != nil { return err } default: - return errUnknownType + panic(fmt.Errorf("unknown type for flag -%s", p.Name)) } return nil } +func (p *_Parser) Reset() { + ifc, typ, kind := p.Target.Interface(), p.Target.Type(), p.Target.Kind() + switch ifc.(type) { + case bool, *bool: + if kind != reflect.Pointer { + } else { + p.Target.Set(reflect.Zero(typ)) + } + case int, *int: + if kind != reflect.Pointer { + } else { + p.Target.Set(reflect.Zero(typ)) + } + case uint, *uint: + if kind != reflect.Pointer { + } else { + p.Target.Set(reflect.Zero(typ)) + } + case int64, *int64: + if kind != reflect.Pointer { + } else { + p.Target.Set(reflect.Zero(typ)) + } + case uint64, *uint64: + if kind != reflect.Pointer { + } else { + p.Target.Set(reflect.Zero(typ)) + } + case int32, *int32: + if kind != reflect.Pointer { + } else { + p.Target.Set(reflect.Zero(typ)) + } + case uint32, *uint32: + if kind != reflect.Pointer { + } else { + p.Target.Set(reflect.Zero(typ)) + } + case string, *string: + if kind != reflect.Pointer { + } else { + p.Target.Set(reflect.Zero(typ)) + } + case float64, *float64: + if kind != reflect.Pointer { + } else { + p.Target.Set(reflect.Zero(typ)) + } + case float32, *float32: + if kind != reflect.Pointer { + } else { + p.Target.Set(reflect.Zero(typ)) + } + case time.Duration, *time.Duration: + if kind != reflect.Pointer { + } else { + p.Target.Set(reflect.Zero(typ)) + } + case flag.Value: + case func(string) error: + case func(string, string) error: + default: + panic(fmt.Errorf("unknown type for flag -%s", p.Name)) + } +} + func (p *_Parser) SetDefault() { if p.DefaultOK { if e := p.Set(p.Default); e != nil { panic(fmt.Errorf("unable to set default value for flag -%s: %w", p.Name, e)) } + return } - p.Target.Set(reflect.Zero(p.Target.Type())) }