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