Skip to content

Commit

Permalink
add doNotRecurseSignalErr
Browse files Browse the repository at this point in the history
  • Loading branch information
suqin-haha authored and muir committed Aug 23, 2024
1 parent 84b0800 commit 58aedaa
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 9 deletions.
35 changes: 31 additions & 4 deletions example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,14 +49,34 @@ func makeIntDoublerWithError(t reflect.Type) (func(v reflect.Value), error) {
panic("makeIntDoublerWithError only supports pointers to structs")
}
var ints []reflect.StructField
err := reflectutils.WalkStructElementsWithError(t, func(f reflect.StructField) (bool, error) {
err := reflectutils.WalkStructElementsWithError(t, func(f reflect.StructField) error {
if f.Type.Kind() == reflect.Int {
ints = append(ints, f)
}
if f.Type.Kind() == reflect.String {
return false, errors.Errorf("error on string")
return errors.Errorf("error on string, and stop walk")
}
return true, nil
return nil // default nil recursive down
})
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 makeIntDoublerNoRecursive(t reflect.Type) (func(v reflect.Value), error) {
if t.Kind() != reflect.Ptr || t.Elem().Kind() != reflect.Struct {
panic("makeIntDoublerNoRecursive only supports pointers to structs")
}
var ints []reflect.StructField
err := reflectutils.WalkStructElementsWithError(t, func(f reflect.StructField) error {
if f.Type.Kind() == reflect.Int {
ints = append(ints, f)
}
return reflectutils.DoNotRecurseSignalErr
})
return func(v reflect.Value) {
v = v.Elem()
Expand Down Expand Up @@ -84,7 +104,14 @@ func Example() {
doubler(v)
fmt.Printf("%v\n", v.Interface())

doubler, err := makeIntDoublerNoRecursive(v.Type())
doubler(v)
fmt.Printf("%v\n", v.Interface())
fmt.Printf("%v", err)

// Output: &{6 {4 12} string {10}}
// &{12 {4 12} string {10}}
// <nil>
}

func ExampleError() {
Expand All @@ -106,5 +133,5 @@ func ExampleError() {
fmt.Printf("%v", err)

// Output: &{6 {4 12} string {5}}
// error on string
// error on string, and stop walk
}
34 changes: 29 additions & 5 deletions walkstruct.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package reflectutils

import (
"errors"
"reflect"
)

Expand Down Expand Up @@ -37,7 +38,23 @@ func doWalkStructElements(t reflect.Type, path []int, f func(reflect.StructField
}
}

func WalkStructElementsWithError(t reflect.Type, f func(reflect.StructField) (bool, error)) error {
// WalkStructElementsWithError recursively visits the fields in a structure calling a
// callback for each field. It modifies the reflect.StructField object
// so that Index is relative to the root object originally passed to
// WalkStructElementsWithError. This allows the FieldByIndex method on a reflect.Value
// matching the original struct to fetch that field.
//
// WalkStructElementsWithError should be called with a reflect.Type whose Kind() is
// reflect.Struct or whose Kind() is reflect.Ptr and Elem.Type() is reflect.Struct.
// All other types will simply be ignored.
//
// A non-nil return value from the called function stops iteration and recursion and becomes
// the return value.
//
// A special error return value, [DoNotRecurseSignalErr] is not considered an error (it will
// not become the return value, and it does not stop iteration) but it will stop recursion if returned
// on a field that is itself a struct.
func WalkStructElementsWithError(t reflect.Type, f func(reflect.StructField) error) error {
if t.Kind() == reflect.Struct {
return doWalkStructElementsWithError(t, []int{}, f)
}
Expand All @@ -47,16 +64,23 @@ func WalkStructElementsWithError(t reflect.Type, f func(reflect.StructField) (bo
return nil
}

func doWalkStructElementsWithError(t reflect.Type, path []int, f func(reflect.StructField) (bool, error)) error {
var DoNotRecurseSignalErr = errors.New("walkstruct: do not recurse signal")

func doWalkStructElementsWithError(t reflect.Type, path []int, f func(reflect.StructField) 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 {
err := f(field)
if errors.Is(err, DoNotRecurseSignalErr) {
continue
}
if err != nil {
return err
} else if walkDown && field.Type.Kind() == reflect.Struct {
err := doWalkStructElementsWithError(field.Type, np, f)
}
if field.Type.Kind() == reflect.Struct {
err = doWalkStructElementsWithError(field.Type, np, f)
if err != nil {
return err
}
Expand Down

0 comments on commit 58aedaa

Please sign in to comment.