Compare commits

...

3 Commits

Author SHA1 Message Date
Alex Flint 1f90c779f2 put back call to Addr() 2021-04-19 22:32:47 -07:00
Alex Flint a081287ba6 drop extraneous log 2021-04-19 22:27:42 -07:00
Alex Flint 3a035a19bd deal with chan, map, and slice types that implement TextUnmarshaler 2021-04-19 22:23:18 -07:00
2 changed files with 75 additions and 4 deletions

View File

@ -31,6 +31,40 @@ func Parse(dest interface{}, s string) error {
return ParseValue(reflect.ValueOf(dest), s)
}
func parseAsTextUnmarshaler(v reflect.Value, s string) (bool, error) {
t := v.Type()
if !t.Implements(textUnmarshalerType) {
return false, nil
}
if v.IsNil() && v.CanSet() {
switch t.Kind() {
case reflect.Ptr:
v.Set(reflect.New(v.Type().Elem()))
case reflect.Slice:
v.Set(reflect.MakeSlice(t, 0, 0))
case reflect.Map:
v.Set(reflect.MakeMap(t))
case reflect.Chan:
v.Set(reflect.MakeChan(t, 0))
}
}
if !v.IsNil() && t.Kind() == reflect.Ptr {
switch t.Elem().Kind() {
case reflect.Slice:
v.Elem().Set(reflect.MakeSlice(t.Elem(), 0, 0))
case reflect.Map:
v.Elem().Set(reflect.MakeMap(t.Elem()))
case reflect.Chan:
v.Elem().Set(reflect.MakeChan(t.Elem(), 0))
}
}
err := v.Interface().(encoding.TextUnmarshaler).UnmarshalText([]byte(s))
return true, err
}
// ParseValue assigns a value to v by parsing s.
func ParseValue(v reflect.Value, s string) error {
// If we have a nil pointer then allocate a new object
@ -43,14 +77,15 @@ func ParseValue(v reflect.Value, s string) error {
}
// If it implements encoding.TextUnmarshaler then use that
if scalar, ok := v.Interface().(encoding.TextUnmarshaler); ok {
return scalar.UnmarshalText([]byte(s))
if matched, err := parseAsTextUnmarshaler(v, s); matched {
return err
}
// If it's a value instead of a pointer, check that we can unmarshal it
// via TextUnmarshaler as well
if v.CanAddr() {
if scalar, ok := v.Addr().Interface().(encoding.TextUnmarshaler); ok {
return scalar.UnmarshalText([]byte(s))
if matched, err := parseAsTextUnmarshaler(v.Addr(), s); matched {
return err
}
}

View File

@ -87,3 +87,39 @@ func TestParse(t *testing.T) {
require.NoError(t, err)
assert.Equal(t, 123, v)
}
type sliceUnmarshaler []int
func (sliceUnmarshaler) UnmarshalText(b []byte) error {
return nil
}
type mapUnmarshaler map[string]string
func (m mapUnmarshaler) UnmarshalText(b []byte) error {
m["a"] = string(b)
return nil
}
type chanUnmarshaler chan string
func (ch chanUnmarshaler) UnmarshalText(b []byte) error {
return nil
}
func TestParseReferenceTypes(t *testing.T) {
var err error
var s sliceUnmarshaler
err = Parse(&s, "test1")
require.NoError(t, err)
var m mapUnmarshaler
err = Parse(&m, "test2")
require.NoError(t, err)
assert.Equal(t, "test2", m["a"])
var c chanUnmarshaler
err = Parse(&c, "test3")
require.NoError(t, err)
}