diff --git a/find.go b/find.go new file mode 100644 index 0000000000000000000000000000000000000000..e1a262b18e030f650a1f2d1c1e75e9c8f836de26 --- /dev/null +++ b/find.go @@ -0,0 +1,53 @@ +package pathfinder + +import ( + "fmt" + "reflect" + "strings" +) + +func FindPaths(v reflect.Value, targetType reflect.Type, path []string, paths *[]string) { + + if v.Kind() == reflect.Invalid { + return + } + + vType := v.Type() + + switch v.Kind() { + case reflect.Ptr: + FindPaths(v.Elem(), targetType, path, paths) + case reflect.Struct: + for i := 0; i < v.NumField(); i++ { + newPath := append(path, vType.Field(i).Name) + FindPaths(v.Field(i), targetType, newPath, paths) + } + case reflect.Map: + for _, key := range v.MapKeys() { + newPath := append(path, fmt.Sprint(key)) + FindPaths(v.MapIndex(key), targetType, newPath, paths) + if v.MapIndex(key).Type() == targetType { + *paths = append(*paths, strings.Join(newPath, ".")) + } + } + case reflect.Slice, reflect.Array: + for i := 0; i < v.Len(); i++ { + newPath := append(path, fmt.Sprint(i)) + FindPaths(v.Index(i), targetType, newPath, paths) + if v.Index(i).Type() == targetType { + *paths = append(*paths, strings.Join(newPath, ".")) + } + } + case reflect.String: + if vType != targetType { + return + } + + default: + return + } + + if vType == targetType { + *paths = append(*paths, strings.Join(path, ".")) + } +} diff --git a/find_test.go b/find_test.go new file mode 100644 index 0000000000000000000000000000000000000000..367a33ef2d6406d2ef4167b6005af15b1b32d66f --- /dev/null +++ b/find_test.go @@ -0,0 +1,60 @@ +package pathfinder + +import ( + "reflect" + "testing" +) + +type TestStruct struct { + Field1 string + Field2 int + Inner struct { + SubField1 float64 + SubField2 string + } +} + +func TestFindPaths(t *testing.T) { + tests := []struct { + input interface{} + targetType reflect.Type + expectedPaths []string + }{ + { + TestStruct{"hello", 42, struct { + SubField1 float64 + SubField2 string + }{3.14, "world"}}, + reflect.TypeOf(""), + []string{"Field1", "Inner.SubField2"}, + }, + { + []int{1, 2, 3}, + reflect.TypeOf(0), + []string{"0", "1", "2"}, + }, + { + map[string]int{"key1": 1, "key2": 2}, + reflect.TypeOf(0), + []string{"key1", "key2"}, + }, + { + nil, + reflect.TypeOf(""), + []string{}, + }, + } + + for i, test := range tests { + var paths []string + FindPaths(reflect.ValueOf(test.input), test.targetType, []string{}, &paths) + + if len(paths) == 0 && len(test.expectedPaths) == 0 { + continue + } + + if !reflect.DeepEqual(paths, test.expectedPaths) { + t.Errorf("Test case %d failed, expected %v, got %v", i+1, test.expectedPaths, paths) + } + } +} diff --git a/issue_13_test.go b/issue_13_test.go new file mode 100644 index 0000000000000000000000000000000000000000..b4c2bbf487d55d9245c0795e9badb10893443cd8 --- /dev/null +++ b/issue_13_test.go @@ -0,0 +1 @@ +package pathfinder