Skip to content

Commit

Permalink
Merge pull request #2 from goinsane/develop
Browse files Browse the repository at this point in the history
v0.2.0
  • Loading branch information
orkunkaraduman authored Nov 1, 2022
2 parents 4c8e862 + 4f3897f commit c909a62
Show file tree
Hide file tree
Showing 4 changed files with 119 additions and 18 deletions.
37 changes: 37 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
3 changes: 0 additions & 3 deletions errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,3 @@ func numError(err error) error {
}
return err
}

// errUnknownType is internal use only.
var errUnknownType = errors.New("unknown type")
14 changes: 10 additions & 4 deletions examples/example1.go → examples/example1/main.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
//go:build ignore
// +build ignore

package main

import (
Expand All @@ -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)
}
83 changes: 72 additions & 11 deletions flagbind.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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()))
}

0 comments on commit c909a62

Please sign in to comment.