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)) {
 | 
			
		||||
	val := reflect.ValueOf(x)
 | 
			
		||||
	field := val.Field(0)
 | 
			
		||||
	fn(field.String())
 | 
			
		||||
	val := getValue(x)
 | 
			
		||||
 | 
			
		||||
	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
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"testing"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestWalk(t *testing.T) {
 | 
			
		||||
	t.Run("walk function test", func(t *testing.T) {
 | 
			
		||||
		expected := "Chris"
 | 
			
		||||
		var got []string
 | 
			
		||||
type Profile struct {
 | 
			
		||||
	Age  int
 | 
			
		||||
	City string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		x := struct {
 | 
			
		||||
type Person struct {
 | 
			
		||||
	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)
 | 
			
		||||
			})
 | 
			
		||||
 | 
			
		||||
		if len(got) != 1 {
 | 
			
		||||
			t.Errorf("wrong number of function calls, got %d want %d", len(got), 1)
 | 
			
		||||
		}
 | 
			
		||||
		if got[0] != expected {
 | 
			
		||||
			t.Errorf("got %q want %q", got[0], expected)
 | 
			
		||||
			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
 | 
			
		||||
		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)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user