Skip to content

Instantly share code, notes, and snippets.

@punmechanic
Created January 7, 2022 04:45
Show Gist options
  • Save punmechanic/8a51a7bbb6b6300dde6dc9d915181390 to your computer and use it in GitHub Desktop.
Save punmechanic/8a51a7bbb6b6300dde6dc9d915181390 to your computer and use it in GitHub Desktop.
crimes were committed to implement a small version of binding flags to an arbitrary struct
package bind_flags
import (
"reflect"
"github.com/spf13/pflag"
)
type Target struct {
Foo string `flag:"flag-name"`
}
// BindFlags binds the flags in the set to the given destination struct.
//
// dst MUST be a struct containing fields decorated with the flag tag.
// This is a close copy of what Cobra does with Viper, but without an entirely new library.
func BindFlags(dst interface{}, set *pflag.FlagSet) error {
// No need to do anything if we have no flags
if !set.HasFlags() {
return nil
}
// As this is not intended to be re-usable, we are not going to perform much validation here
// If you encounter cryptic errors at runtime, consult tests for usage
t := reflect.TypeOf(dst)
v := reflect.ValueOf(dst)
if t.Kind() == reflect.Ptr {
t = t.Elem()
v = v.Elem()
}
if t.Kind() != reflect.Struct {
panic("dst must be a pointer to a struct")
}
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
// We use v so that we can assign. We need to use t to get the struct tag information.
v2 := v.Field(i)
name, ok := field.Tag.Lookup("flag")
if !ok || !v2.CanSet() {
continue
}
// We only support a limited number of types, because we don't need to support everything
switch field.Type.Kind() {
case reflect.String:
a, err := set.GetString(name)
if err != nil {
return err
}
v2.SetString(a)
case reflect.Uint:
a, err := set.GetUint(name)
if err != nil {
return err
}
v2.SetUint(uint64(a))
}
}
return nil
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment