From b9d29fd10c3df593a6f2e30b22e6100018ee6ef5 Mon Sep 17 00:00:00 2001 From: Volker Schukai <volker.schukai@schukai.com> Date: Sat, 16 Sep 2023 14:12:25 +0200 Subject: [PATCH] feat: new find method for paths #12 --- find.go | 53 ++++++++++++++++++++++++++++++++++++++++++ find_test.go | 60 ++++++++++++++++++++++++++++++++++++++++++++++++ issue_13_test.go | 1 + 3 files changed, 114 insertions(+) create mode 100644 find.go create mode 100644 find_test.go create mode 100644 issue_13_test.go diff --git a/find.go b/find.go new file mode 100644 index 0000000..e1a262b --- /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 0000000..367a33e --- /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 0000000..b4c2bbf --- /dev/null +++ b/issue_13_test.go @@ -0,0 +1 @@ +package pathfinder -- GitLab