Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

improve tag fill by position and walk elements #22

Merged
merged 6 commits into from
Aug 23, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 8 additions & 7 deletions example_parsetag_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,12 @@ type ExampleStruct struct {
}

type TagExtractorType struct {
Name string `pt:"0"`
Square bool `pt:"square"`
Jump bool `pt:"jump"`
Ignore string `pt:"-"`
N int
Name string `pt:"0"` // selected by position will exclude from rest
NameAgain bool `pt:"something,different"`
Square bool `pt:"square"`
Jump bool `pt:"jump"`
Ignore string `pt:"-"`
N int
}

// Fill is a helper for when you're working with tags.
Expand All @@ -33,6 +34,6 @@ func ExampleTag_Fill() {
fmt.Printf("%s: %+v\n", f.Name, tet)
return true
})
// Output: String: {Name:something Square:true Jump:false Ignore: N:9}
// Bar: {Name:different Square:false Jump:true Ignore: N:0}
// Output: String: {Name:something NameAgain:false Square:true Jump:false Ignore: N:9}
// Bar: {Name:different NameAgain:false Square:false Jump:true Ignore: N:0}
}
41 changes: 41 additions & 0 deletions example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"reflect"

"github.com/muir/reflectutils"
"github.com/pkg/errors"
)

type S struct {
Expand Down Expand Up @@ -37,6 +38,28 @@ func makeIntDoubler(t reflect.Type) func(v reflect.Value) {
}
}

func makeIntDoublerWithError(t reflect.Type) (func(v reflect.Value), error) {
if t.Kind() != reflect.Ptr || t.Elem().Kind() != reflect.Struct {
panic("makeIntDoublerWithError only supports pointers to structs")
}
var ints []reflect.StructField
err := reflectutils.WalkStructElementsWithError(t, func(f reflect.StructField) (bool, error) {
if f.Type.Kind() == reflect.Int {
ints = append(ints, f)
} else {
return false, errors.Errorf("not allow element is non-Int")
}
return true, nil
})
return func(v reflect.Value) {
v = v.Elem()
for _, f := range ints {
i := v.FieldByIndex(f.Index)
i.SetInt(int64(i.Interface().(int)) * 2)
}
}, err
}

func Example() {
s := S{
I1: 3,
Expand All @@ -52,3 +75,21 @@ func Example() {

// Output: &{6 string {10}}
}

func ExampleError() {
s := S{
I1: 3,
S: "string",
M: T{
I2: 5,
},
}
v := reflect.ValueOf(&s)
doubler, err := makeIntDoublerWithError(v.Type())
doubler(v)
fmt.Printf("%v\n", v.Interface())
fmt.Printf("%v", err)

// Output: &{6 string {5}}
// not allow element is non-Int
}
6 changes: 3 additions & 3 deletions parsetag.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ var aTagRE = regexp.MustCompile(`(\S+):"((?:[^"\\]|\\.)*)"(?:\s+|$)`)
// type Foo struct {
// S string `json:"s,omitempty" xml:"s_thing"`
// }
//
type Tag struct {
Tag string
Value string
Expand Down Expand Up @@ -105,8 +104,8 @@ func LookupTag(tags reflect.StructTag, tag string) (Tag, bool) {
// The tag being parsed is the receiver (tag). The model that controls the parsing
// is the function parameter (model). The parsing may be adjusted based on the opts.
//
// type MyTags struct {
// Name string `pt:"0"`
// type MyTags struct {
// Name string `pt:"0"` // selected by position will exclude from rest match
suqin-haha marked this conversation as resolved.
Show resolved Hide resolved
// Flag bool `pt:"flag"`
// Int int `pt:"intValue"`
// }
Expand Down Expand Up @@ -176,6 +175,7 @@ func (tag Tag) Fill(model interface{}, opts ...FillOptArg) error {
return true
}
value = elements[i]
delete(kv, value) // exclude from rest match
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@muir This probably not good enough

for case

struct {
    	T6 bool   `xyz:"first,first" want:"{\"Name\":\"first\",\"First\":true}"`
        T7 bool   `xyz:"first,!first" want:"{\"Name\":\"first\",\"First\":false}"`
}

with this line delete(kv, value) will make second first never match. But without this line, will make the second first always match.

} else {
if isBool {
for _, p := range parts {
Expand Down
27 changes: 26 additions & 1 deletion walkstruct.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (
// matching the original struct to fetch that field.
//
// WalkStructElements should be called with a reflect.Type whose Kind() is
// reflect.Struct or whose Kind() is reflec.Ptr and Elme.Type() is reflect.Struct.
// reflect.Struct or whose Kind() is reflect.Ptr and Elem.Type() is reflect.Struct.
// All other types will simply be ignored.
//
// The return value from f only matters when the type of the field is a struct. In
Expand All @@ -37,6 +37,31 @@ func doWalkStructElements(t reflect.Type, path []int, f func(reflect.StructField
}
}

suqin-haha marked this conversation as resolved.
Show resolved Hide resolved
func WalkStructElementsWithError(t reflect.Type, f func(reflect.StructField) (bool, error)) error {
suqin-haha marked this conversation as resolved.
Show resolved Hide resolved
if t.Kind() == reflect.Struct {
return doWalkStructElementsWithError(t, []int{}, f)
}
if t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct {
return doWalkStructElementsWithError(t.Elem(), []int{}, f)
}
return nil
}

func doWalkStructElementsWithError(t reflect.Type, path []int, f func(reflect.StructField) (bool, error)) error {
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
np := copyIntSlice(path)
np = append(np, field.Index...)
field.Index = np
if walkDown, err := f(field); err != nil {
return err
} else if walkDown && field.Type.Kind() == reflect.Struct {
return doWalkStructElementsWithError(field.Type, np, f)
suqin-haha marked this conversation as resolved.
Show resolved Hide resolved
}
}
return nil
}

func copyIntSlice(in []int) []int {
c := make([]int, len(in), len(in)+1)
copy(c, in)
Expand Down