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"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Profile struct {
 | 
				
			||||||
 | 
						Age  int
 | 
				
			||||||
 | 
						City string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Person struct {
 | 
				
			||||||
 | 
						Name    string
 | 
				
			||||||
 | 
						Profile Profile
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestWalk(t *testing.T) {
 | 
					func TestWalk(t *testing.T) {
 | 
				
			||||||
	t.Run("walk function test", func(t *testing.T) {
 | 
						cases := []struct {
 | 
				
			||||||
		expected := "Chris"
 | 
							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)
 | 
				
			||||||
 | 
								})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if !reflect.DeepEqual(got, test.ExpectedCalls) {
 | 
				
			||||||
 | 
									t.Errorf("got %v want %v", got, test.ExpectedCalls)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						t.Run("with maps", func(t *testing.T) {
 | 
				
			||||||
 | 
							aMap := map[string]string{
 | 
				
			||||||
 | 
								"Cow":   "meuh",
 | 
				
			||||||
 | 
								"Sheep": "meh",
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		var got []string
 | 
							var got []string
 | 
				
			||||||
 | 
							Walk(aMap, func(input string) {
 | 
				
			||||||
		x := struct {
 | 
					 | 
				
			||||||
			Name string
 | 
					 | 
				
			||||||
		}{expected}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		Walk(x, func(input string) {
 | 
					 | 
				
			||||||
			got = append(got, input)
 | 
								got = append(got, input)
 | 
				
			||||||
		})
 | 
							})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if len(got) != 1 {
 | 
							assertContains(t, got, "meuh")
 | 
				
			||||||
			t.Errorf("wrong number of function calls, got %d want %d", len(got), 1)
 | 
							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)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if got[0] != expected {
 | 
						})
 | 
				
			||||||
			t.Errorf("got %q want %q", got[0], expected)
 | 
					
 | 
				
			||||||
 | 
						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)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user