Compare commits
14 Commits
b332088638
...
a0bad4d6e9
Author | SHA1 | Date | |
---|---|---|---|
|
a0bad4d6e9 | ||
|
ab7818e9ea | ||
|
574532f484 | ||
|
10a93aeb27 | ||
|
61a0d0ff03 | ||
|
f64d75ca45 | ||
|
799d730d14 | ||
|
1c3ad9b9d8 | ||
|
049c346e63 | ||
|
80959f822b | ||
|
30a408c9e4 | ||
|
8026475424 | ||
|
122770ae5c | ||
|
766d7449e8 |
47
walk/walk.go
47
walk/walk.go
@ -5,7 +5,48 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func Walk(x interface{}, fn func(string)) {
|
func Walk(x interface{}, fn func(string)) {
|
||||||
val := reflect.ValueOf(x)
|
val := getValue(x)
|
||||||
field := val.Field(0)
|
|
||||||
fn(field.String())
|
walkValue := func(value reflect.Value) {
|
||||||
|
Walk(value.Interface(), fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch val.Kind() {
|
||||||
|
case reflect.String:
|
||||||
|
fn(val.String())
|
||||||
|
case reflect.Struct:
|
||||||
|
for i := 0; i < val.NumField(); i++ {
|
||||||
|
// XXX: Interface() to get interface
|
||||||
|
walkValue(val.Field(i))
|
||||||
|
}
|
||||||
|
case reflect.Slice, reflect.Array:
|
||||||
|
for i := 0; i < val.Len(); i++ {
|
||||||
|
walkValue(val.Index(i))
|
||||||
|
}
|
||||||
|
case reflect.Map:
|
||||||
|
for _, key := range val.MapKeys() {
|
||||||
|
walkValue(val.MapIndex(key))
|
||||||
|
}
|
||||||
|
case reflect.Chan:
|
||||||
|
for {
|
||||||
|
if v, ok := val.Recv(); ok {
|
||||||
|
walkValue(v)
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case reflect.Func:
|
||||||
|
valFnResult := val.Call(nil)
|
||||||
|
for _, res := range valFnResult {
|
||||||
|
walkValue(res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getValue(x interface{}) reflect.Value {
|
||||||
|
val := reflect.ValueOf(x)
|
||||||
|
if val.Kind() == reflect.Pointer {
|
||||||
|
return val.Elem()
|
||||||
|
}
|
||||||
|
return val
|
||||||
}
|
}
|
||||||
|
@ -1,27 +1,147 @@
|
|||||||
package walk
|
package walk
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestWalk(t *testing.T) {
|
type Profile struct {
|
||||||
t.Run("walk function test", func(t *testing.T) {
|
Age int
|
||||||
expected := "Chris"
|
City string
|
||||||
var got []string
|
}
|
||||||
|
|
||||||
x := struct {
|
type Person struct {
|
||||||
Name string
|
Name string
|
||||||
}{expected}
|
Profile Profile
|
||||||
|
}
|
||||||
|
|
||||||
Walk(x, func(input string) {
|
func TestWalk(t *testing.T) {
|
||||||
|
cases := []struct {
|
||||||
|
Name string
|
||||||
|
Input interface{}
|
||||||
|
ExpectedCalls []string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"struct with one string field",
|
||||||
|
struct {
|
||||||
|
Name string
|
||||||
|
}{"Chris"},
|
||||||
|
[]string{"Chris"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"struct with two string fields",
|
||||||
|
struct {
|
||||||
|
Name string
|
||||||
|
City string
|
||||||
|
}{"Chris", "London"},
|
||||||
|
[]string{"Chris", "London"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"struct with non string field",
|
||||||
|
struct {
|
||||||
|
Name string
|
||||||
|
Age int
|
||||||
|
}{"Chris", 29},
|
||||||
|
[]string{"Chris"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"nested fields",
|
||||||
|
Person{"Chris", Profile{29, "London"}},
|
||||||
|
[]string{"Chris", "London"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pointer to things",
|
||||||
|
&Person{"Chris", Profile{29, "London"}},
|
||||||
|
[]string{"Chris", "London"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slices",
|
||||||
|
[]Profile{
|
||||||
|
{29, "London"},
|
||||||
|
{33, "Paris"},
|
||||||
|
},
|
||||||
|
[]string{"London", "Paris"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"arrays",
|
||||||
|
[2]Profile{
|
||||||
|
{29, "London"},
|
||||||
|
{33, "Paris"},
|
||||||
|
},
|
||||||
|
[]string{"London", "Paris"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range cases {
|
||||||
|
t.Run(test.Name, func(t *testing.T) {
|
||||||
|
var got []string
|
||||||
|
Walk(test.Input, func(input string) {
|
||||||
got = append(got, input)
|
got = append(got, input)
|
||||||
})
|
})
|
||||||
|
|
||||||
if len(got) != 1 {
|
if !reflect.DeepEqual(got, test.ExpectedCalls) {
|
||||||
t.Errorf("wrong number of function calls, got %d want %d", len(got), 1)
|
t.Errorf("got %v want %v", got, test.ExpectedCalls)
|
||||||
}
|
}
|
||||||
if got[0] != expected {
|
})
|
||||||
t.Errorf("got %q want %q", got[0], expected)
|
}
|
||||||
|
|
||||||
|
t.Run("with maps", func(t *testing.T) {
|
||||||
|
aMap := map[string]string{
|
||||||
|
"Cow": "meuh",
|
||||||
|
"Sheep": "meh",
|
||||||
|
}
|
||||||
|
var got []string
|
||||||
|
Walk(aMap, func(input string) {
|
||||||
|
got = append(got, input)
|
||||||
|
})
|
||||||
|
|
||||||
|
assertContains(t, got, "meuh")
|
||||||
|
assertContains(t, got, "meh")
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("with channels", func(t *testing.T) {
|
||||||
|
aChannel := make(chan Profile)
|
||||||
|
go func() {
|
||||||
|
aChannel <- Profile{21, "Berlin"}
|
||||||
|
aChannel <- Profile{28, "Beijing"}
|
||||||
|
close(aChannel)
|
||||||
|
}()
|
||||||
|
var got []string
|
||||||
|
want := []string{"Berlin", "Beijing"}
|
||||||
|
Walk(aChannel, func(input string) {
|
||||||
|
got = append(got, input)
|
||||||
|
})
|
||||||
|
if !reflect.DeepEqual(got, want) {
|
||||||
|
t.Errorf("got %v want %v", got, want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("with functions", func(t *testing.T) {
|
||||||
|
aFunction := func() (Profile, Profile) {
|
||||||
|
return Profile{21, "Berlin"}, Profile{28, "Beijing"}
|
||||||
|
}
|
||||||
|
|
||||||
|
var got []string
|
||||||
|
want := []string{"Berlin", "Beijing"}
|
||||||
|
Walk(aFunction, func(input string) {
|
||||||
|
got = append(got, input)
|
||||||
|
})
|
||||||
|
if !reflect.DeepEqual(got, want) {
|
||||||
|
t.Errorf("got %v want %v", got, want)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func assertContains(t testing.TB, haystack []string, needle string) {
|
||||||
|
t.Helper()
|
||||||
|
contains := false
|
||||||
|
|
||||||
|
for _, x := range haystack {
|
||||||
|
if x == needle {
|
||||||
|
contains = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !contains {
|
||||||
|
t.Errorf("expected %v to contain %q but it didn't", haystack, needle)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user