From b751118d09289824e8a63fad77725f161d083760 Mon Sep 17 00:00:00 2001 From: Oleg Kovalov Date: Sat, 21 May 2022 13:01:05 +0200 Subject: [PATCH] Add TextMarshaler --- example_test.go | 17 +++++++++++++++++ flagx.go | 15 +++++++++++++++ utils.go | 44 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 76 insertions(+) create mode 100644 utils.go diff --git a/example_test.go b/example_test.go index 12d67fb..0a56834 100644 --- a/example_test.go +++ b/example_test.go @@ -2,6 +2,7 @@ package flagx_test import ( "fmt" + "net" "os" "time" @@ -22,3 +23,19 @@ func ExampleFlagSet() { panic(fmt.Sprintf("got %v want %v", d, 20*time.Second)) } } + +func ExampleTextVar() { + fs := flagx.NewFlagSet("ExampleTextVar", os.Stdout) + + var ip net.IP + fs.Text(&ip, "ip", "", net.IPv4(192, 168, 0, 100), "`IP address` to parse") + + err := fs.Parse([]string{"-ip", "127.0.0.1"}) + if err != nil { + panic(err) + } + fmt.Printf("{ip: %v}\n\n", ip) + + // Output: + // {ip: 127.0.0.1} +} diff --git a/flagx.go b/flagx.go index e1f98d0..725ad89 100644 --- a/flagx.go +++ b/flagx.go @@ -1,6 +1,7 @@ package flagx import ( + "encoding" "flag" "io" "time" @@ -80,6 +81,20 @@ func (f *FlagSet) Func(name, alias, usage string, fn func(string) error) { } } +// Text defines a flag with the specified name and usage string. +// The argument p must be a pointer to a variable that will hold the value +// of the flag, and p must implement encoding.TextUnmarshaler. +// If the flag is used, the flag value will be passed to p's UnmarshalText method. +// The type of the default value must be the same as the type of p. +// Empty string for alias means no alias will be created. +func (f *FlagSet) Text(p encoding.TextUnmarshaler, name, alias string, value encoding.TextMarshaler, usage string) { + // TODO(cristaloleg): for Go 1.19 this can be f.fs.TextVar(...) + f.fs.Var(newTextValue(value, p), name, usage) + if alias != "" { + f.fs.Var(newTextValue(value, p), alias, usage) + } +} + // Bool defines a bool flag with specified name, alias, default value, and usage string. // The argument p points to a bool variable in which to store the value of the flag. // Empty string for alias means no alias will be created. diff --git a/utils.go b/utils.go new file mode 100644 index 0000000..1c7dc05 --- /dev/null +++ b/utils.go @@ -0,0 +1,44 @@ +package flagx + +import ( + "encoding" + "fmt" + "reflect" +) + +type textValue struct { + val encoding.TextUnmarshaler +} + +func newTextValue(val encoding.TextMarshaler, p encoding.TextUnmarshaler) textValue { + ptrVal := reflect.ValueOf(p) + if ptrVal.Kind() != reflect.Ptr { + panic("flagx: variable value type must be a pointer") + } + defVal := reflect.ValueOf(val) + if defVal.Kind() == reflect.Ptr { + defVal = defVal.Elem() + } + if defVal.Type() != ptrVal.Type().Elem() { + panic(fmt.Sprintf("flagx: default type does not match variable type: %v != %v", defVal.Type(), ptrVal.Type().Elem())) + } + ptrVal.Elem().Set(defVal) + return textValue{p} +} + +func (v textValue) Set(s string) error { + return v.val.UnmarshalText([]byte(s)) +} + +func (v textValue) Get() interface{} { + return v.val +} + +func (v textValue) String() string { + if m, ok := v.val.(encoding.TextMarshaler); ok { + if b, err := m.MarshalText(); err == nil { + return string(b) + } + } + return "" +}