From 03f17f5cdf9c3d2dceed1ad20ee87b125ee0feb3 Mon Sep 17 00:00:00 2001 From: David Sharnoff Date: Sun, 7 Nov 2021 22:03:33 -0800 Subject: [PATCH] can now split tag values --- parsetag.go | 42 ++++++++++++++++++++++++++++++------------ parsetag_test.go | 26 ++++++++++++++++++++++++++ unpack.go | 24 +++++++++++++++++++++--- 3 files changed, 77 insertions(+), 15 deletions(-) diff --git a/parsetag.go b/parsetag.go index f4006fc..22a6171 100644 --- a/parsetag.go +++ b/parsetag.go @@ -127,29 +127,47 @@ func (tag Tag) Fill(model interface{}, opts ...FillOptArg) error { var walkErr error WalkStructElements(v.Type(), func(f reflect.StructField) bool { tag := f.Tag.Get(opt.tag) - name := f.Name if tag == "-" { return false } count++ - i, err := strconv.Atoi(tag) + parts := strings.Split(tag, ",") var value string - if err == nil { - // positional! - if i >= len(elements) { - return true + if len(parts) > 0 && parts[0] != "" { + i, err := strconv.Atoi(parts[0]) + if err == nil { + // positional! + if i >= len(elements) { + return true + } + value = elements[i] + } else { + value = kv[parts[0]] } - value = elements[i] } else { - if tag != "" { - name = tag - } - value = kv[name] + value = kv[f.Name] } if value == "" { return true } - set, err := MakeStringSetter(f.Type) + var sso []StringSetterArg + if len(parts) > 1 { + for _, part := range parts[1:] { + if strings.HasPrefix(part, "split=") { + splitOn := part[len("split="):] + switch splitOn { + case "comma": + splitOn = "," + case "quote": + splitOn = `"` + case "space": + splitOn = " " + } + sso = append(sso, WithSplitOn(splitOn)) + } + } + } + set, err := MakeStringSetter(f.Type, sso...) if err != nil { walkErr = errors.Wrapf(err, "Cannot set %s", f.Type) return true diff --git a/parsetag_test.go b/parsetag_test.go index b5d5bd4..38776b7 100644 --- a/parsetag_test.go +++ b/parsetag_test.go @@ -1,6 +1,7 @@ package reflectutils_test import ( + "encoding/json" "reflect" "testing" @@ -29,3 +30,28 @@ func ts(t *testing.T, tag reflect.StructTag, want ...string) { } assert.Equal(t, want, s, tag) } + +func TestFill(t *testing.T) { + type tagData struct { + P0 []string `tf:"0,split=space" json:",omitempty"` + } + type testStruct struct { + T1 string `xyz:"a b" want:"{\"P0\":[\"a\",\"b\"]}"` + } + var x testStruct + reflectutils.WalkStructElements(reflect.TypeOf(x), func(f reflect.StructField) bool { + var got tagData + t.Logf("%s: %s", f.Name, f.Tag) + err := reflectutils.SplitTag(f.Tag).Set().Get("xyz").Fill(&got, reflectutils.WithTag("tf")) + if !assert.NoErrorf(t, err, "extract tag %s", f.Name) { + return true + } + var want tagData + err = json.Unmarshal([]byte(f.Tag.Get("want")), &want) + if !assert.NoErrorf(t, err, "extract want %s", f.Name) { + return true + } + assert.Equal(t, want, got, f.Name) + return true + }) +} diff --git a/unpack.go b/unpack.go index 91d984a..6253383 100644 --- a/unpack.go +++ b/unpack.go @@ -11,6 +11,18 @@ import ( var textUnmarshallerType = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem() +type stringSetterOpts struct { + split string +} + +type StringSetterArg func(*stringSetterOpts) + +func WithSplitOn(s string) StringSetterArg { + return func(o *stringSetterOpts) { + o.split = s + } +} + // MakeStringSetter handles setting a reflect.Value from a string. // Based on type, it returns a function to do the work. It is assumed that the // reflect.Type matches the reflect.Value. If not, panic is likely. @@ -22,7 +34,13 @@ var textUnmarshallerType = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem // // Maps, structs, channels, interfaces, channels, and funcs are not supported unless // they happen to implent encoding.TextUnmarshaler. -func MakeStringSetter(t reflect.Type) (func(target reflect.Value, value string) error, error) { +func MakeStringSetter(t reflect.Type, optArgs ...StringSetterArg) (func(target reflect.Value, value string) error, error) { + opts := stringSetterOpts{ + split: ",", + } + for _, f := range optArgs { + f(&opts) + } if t.AssignableTo(textUnmarshallerType) { return func(target reflect.Value, value string) error { p := reflect.New(t.Elem()) @@ -111,7 +129,7 @@ func MakeStringSetter(t reflect.Type) (func(target reflect.Value, value string) return nil, err } return func(target reflect.Value, value string) error { - for i, v := range strings.SplitN(value, ",", target.Cap()) { + for i, v := range strings.SplitN(value, opts.split, target.Cap()) { err := setElem(target.Index(i), v) if err != nil { return err @@ -125,7 +143,7 @@ func MakeStringSetter(t reflect.Type) (func(target reflect.Value, value string) return nil, err } return func(target reflect.Value, value string) error { - values := strings.Split(value, ",") + values := strings.Split(value, opts.split) a := reflect.MakeSlice(target.Type(), len(values), len(values)) for i, v := range values { err := setElem(a.Index(i), v)