From 95b67e571cd7a209aad1cce774a606452c5efc48 Mon Sep 17 00:00:00 2001
From: Volker Schukai <volker.schukai@schukai.com>
Date: Wed, 16 Aug 2023 00:10:34 +0200
Subject: [PATCH] fix: path assignment #7

---
 Taskfile.yml        |   2 +-
 import.go           |  79 ++++++--------------------------
 integration_test.go |   2 +-
 issue-7_test.go     | 108 ++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 123 insertions(+), 68 deletions(-)

diff --git a/Taskfile.yml b/Taskfile.yml
index 7febe83..3465c69 100644
--- a/Taskfile.yml
+++ b/Taskfile.yml
@@ -17,7 +17,7 @@ tasks:
     desc: Conduct fuzzing tests.#
     cmds:  
       - echo "Conduct fuzzing tests."
-      - go test -v -fuzztime=30s -fuzz=Fuzz ./...
+      - go test -v -fuzztime=10s -fuzz=Fuzz ./...
 
   add-licenses:
     desc: Attach license headers to Go files.
diff --git a/import.go b/import.go
index 12bd306..2a83855 100644
--- a/import.go
+++ b/import.go
@@ -94,71 +94,12 @@ func (s *Settings[C]) importStreams() {
 	}
 }
 
-// // replacePath replaces all pathInterface fields in the struct with the given path
-// func replacePath(p string, c any) {
-//
-//	if reflect.TypeOf(c).Kind() != reflect.Ptr {
-//		panic("c must be a pointer")
-//	}
-//
-//	if reflect.TypeOf(c).Elem().Kind() != reflect.Struct {
-//		panic("c must be a pointer to a struct")
-//	}
-//
-//	fields := reflect.VisibleFields(reflect.TypeOf(c).Elem())
-//	for _, field := range fields {
-//
-//		r := reflect.ValueOf(c).Elem().FieldByName(field.Name)
-//		if field.Type.Kind() == reflect.Struct {
-//			if r.CanAddr() {
-//				replacePath(p, r.Addr().Interface())
-//			}
-//			continue
-//		}
-//
-//		_, ok := r.Interface().(pathInterface)
-//		if ok {
-//
-//			if r.CanSet() {
-//				if !path.IsAbs(r.String()) {
-//					r.SetString(path.Join(p, r.String()))
-//				}
-//			}
-//			continue
-//
-//		}
-//
-//		if r.Kind() == reflect.Slice {
-//			for i := 0; i < r.Len(); i++ {
-//				if r.Index(i).CanAddr() {
-//					replacePath(p, r.Index(i).Addr().Interface())
-//				}
-//			}
-//		} else if r.Kind() == reflect.Map {
-//			for _, k := range r.MapKeys() {
-//				if r.MapIndex(k).CanAddr() {
-//					replacePath(p, r.MapIndex(k).Addr().Interface())
-//				}
-//			}
-//		} else if r.Kind() == reflect.Ptr {
-//			if r.Elem().CanAddr() {
-//				replacePath(p, r.Elem().Addr().Interface())
-//			}
-//		} else if r.Kind() == reflect.Interface {
-//			if r.Elem().CanAddr() {
-//				replacePath(p, r.Elem().Addr().Interface())
-//			}
-//		}
-//
-//	}
-//
-// }
 func replacePath(p string, c interface{}) {
 	cValue := reflect.ValueOf(c)
 
 	// If c is of type PathValue (which implements pathInterface), modify the path
-	if cValue.Type().ConvertibleTo(reflect.TypeOf(PathValue(""))) {
-		pathVal := cValue.Convert(reflect.TypeOf(PathValue(""))).Interface().(PathValue)
+	if cValue.Type() == reflect.TypeOf(PathValue("")) {
+		pathVal := cValue.Interface().(PathValue)
 		if !path.IsAbs(pathVal.String()) {
 			newPath := PathValue(path.Join(p, pathVal.String()))
 			if cValue.CanSet() {
@@ -189,8 +130,8 @@ func handleField(p string, r reflect.Value) {
 	case reflect.Slice:
 		for i := 0; i < r.Len(); i++ {
 			elem := r.Index(i)
-			if elem.Type().ConvertibleTo(reflect.TypeOf(PathValue(""))) {
-				pathVal := elem.Convert(reflect.TypeOf(PathValue(""))).Interface().(PathValue)
+			if elem.Type() == reflect.TypeOf(PathValue("")) {
+				pathVal := elem.Interface().(PathValue)
 				if !path.IsAbs(pathVal.String()) {
 					newPath := PathValue(path.Join(p, pathVal.String()))
 					if elem.CanSet() {
@@ -204,8 +145,8 @@ func handleField(p string, r reflect.Value) {
 	case reflect.Map:
 		for _, k := range r.MapKeys() {
 			elem := r.MapIndex(k)
-			if elem.Type().ConvertibleTo(reflect.TypeOf(PathValue(""))) {
-				pathVal := elem.Convert(reflect.TypeOf(PathValue(""))).Interface().(PathValue)
+			if elem.Type() == reflect.TypeOf(PathValue("")) {
+				pathVal := elem.Interface().(PathValue)
 				if !path.IsAbs(pathVal.String()) {
 					newPath := PathValue(path.Join(p, pathVal.String()))
 					r.SetMapIndex(k, reflect.ValueOf(newPath))
@@ -217,9 +158,15 @@ func handleField(p string, r reflect.Value) {
 	default:
 		// Check for pathInterface
 		if v, ok := r.Interface().(pathInterface); ok {
+			// Check if r is nil
+			if r.Kind() == reflect.Ptr && r.IsNil() {
+				return
+			}
 			currentPath := v.String()
 			if r.CanSet() && !path.IsAbs(currentPath) {
-				r.SetString(path.Join(p, currentPath))
+				if r.Type() == reflect.TypeOf(PathValue("")) {
+					r.Set(reflect.ValueOf(PathValue(path.Join(p, currentPath))))
+				}
 			}
 		}
 	}
diff --git a/integration_test.go b/integration_test.go
index 89e6e90..c5f1b92 100644
--- a/integration_test.go
+++ b/integration_test.go
@@ -9,7 +9,7 @@ import (
 	"testing"
 )
 
-func FuzzTest(f *testing.F) {
+func Fuzz1Test(f *testing.F) {
 
 	f.Fuzz(func(t *testing.T, a string, b bool, f int) {
 
diff --git a/issue-7_test.go b/issue-7_test.go
index 781f678..711714f 100644
--- a/issue-7_test.go
+++ b/issue-7_test.go
@@ -87,6 +87,7 @@ type Issue7TestStruct2 struct {
 	B Issue7TestStruct1
 	C []PathValue
 	D map[string]PathValue
+	E string
 }
 
 func TestIssue7ReplacePath(t *testing.T) {
@@ -114,6 +115,7 @@ func TestIssue7ReplacePath(t *testing.T) {
 			"key1": "relkey1",
 			"key2": "relkey2",
 		},
+		E: "justastring",
 	}
 
 	replacePath(basePath, &ts2)
@@ -136,4 +138,110 @@ func TestIssue7ReplacePath(t *testing.T) {
 	if ts2.D["key2"] != PathValue(path.Join(basePath, "relkey2")) {
 		t.Errorf("Expected '%s', got '%s'", path.Join(basePath, "relkey2"), ts2.D["key2"])
 	}
+	if ts2.E != "justastring" {
+		t.Errorf("Expected '%s', got '%s'", "justastring", ts2.E)
+	}
+}
+
+type DeepNestedStruct struct {
+	A PathValue
+	B string
+	C *PathValue
+	D []PathValue
+	E map[string]PathValue
+	F interface{}
+	G *DeepNestedStruct
+	H []DeepNestedStruct
+	I map[string]DeepNestedStruct
+}
+
+type MyStruct struct {
+	A PathValue
+	B string
+	C *PathValue
+	D []PathValue
+	E map[string]PathValue
+	F interface{}
+	G DeepNestedStruct
+	H []DeepNestedStruct
+	I map[string]DeepNestedStruct
+}
+
+func TestReplacePath(t *testing.T) {
+	// Sample seed data to guide the fuzzer
+	tempPathValueC := PathValue("rel/pathC")
+	tempPathValueF := PathValue("rel/pathF")
+	tempPathValueGC := PathValue("rel/pathGC")
+
+	s := &MyStruct{
+		A: "rel/pathA",
+		B: "normalStringB",
+		C: &tempPathValueC,
+		D: []PathValue{"rel1D", "rel2D"},
+		E: map[string]PathValue{
+			"key1E": "relkey1E",
+			"key2E": "relkey2E",
+		},
+		F: tempPathValueF,
+		G: DeepNestedStruct{
+			A: "rel/pathGA",
+			B: "normalStringGB",
+			C: &tempPathValueGC,
+			D: []PathValue{"rel1GD", "rel2GD"},
+			E: map[string]PathValue{
+				"key1GE": "relkey1GE",
+				"key2GE": "relkey2GE",
+			},
+			F: tempPathValueF,
+			H: []DeepNestedStruct{
+				{
+					A: "rel/pathGHA",
+				},
+			},
+			I: map[string]DeepNestedStruct{
+				"key1GI": {
+					A: "rel/pathGIA",
+				},
+			},
+		},
+		H: []DeepNestedStruct{
+			{
+				A: "rel/pathHA",
+			},
+		},
+		I: map[string]DeepNestedStruct{
+			"key1I": {
+				A: "rel/pathIA",
+			},
+		},
+	}
+
+	basePath := "/basepath"
+	// Copy the struct to compare later
+	original := *s
+
+	replacePath(basePath, s)
+
+	// Checking each field to ensure replacePath works as expected
+	if s.A != original.A && !path.IsAbs(s.A.String()) {
+		t.Errorf("Path not replaced correctly for field A")
+	}
+	if s.C != nil && *s.C != *original.C && !path.IsAbs(s.C.String()) {
+		t.Errorf("Path not replaced correctly for field C")
+	}
+	for idx, val := range s.D {
+		if val != original.D[idx] && !path.IsAbs(val.String()) {
+			t.Errorf("Path not replaced correctly for field D at index %d", idx)
+		}
+	}
+	for key, val := range s.E {
+		if val != original.E[key] && !path.IsAbs(val.String()) {
+			t.Errorf("Path not replaced correctly for field E with key %s", key)
+		}
+	}
+	if s.F != original.F && !path.IsAbs(s.F.(PathValue).String()) {
+		t.Errorf("Path not replaced correctly for field F")
+	}
+	// Check fields of the nested struct (G) similarly...
+	// Continue this for all fields and nested fields of MyStruct
 }
-- 
GitLab