From 85d91e1d80f8469997b15a08393b3475caa5808d Mon Sep 17 00:00:00 2001
From: Volker Schukai <volker.schukai@schukai.com>
Date: Wed, 13 Sep 2023 15:36:55 +0200
Subject: [PATCH] feat: set values in a map #5

---
 issue_2_test.go | 33 ++++++++++++++++++++++++++++++++
 set.go          | 50 ++++++++++++++++++++++++++++++++++++++++++++-----
 2 files changed, 78 insertions(+), 5 deletions(-)

diff --git a/issue_2_test.go b/issue_2_test.go
index 1e3ef82..a86b84b 100644
--- a/issue_2_test.go
+++ b/issue_2_test.go
@@ -106,3 +106,36 @@ func TestSetValueWithArray(t *testing.T) {
 	assert.NotNil(t, err)
 
 }
+
+type PathValue string
+
+type SubTestSubPaths struct {
+	Template    PathValue
+	Definitions []PathValue
+}
+
+type SubTest2Def struct {
+	Paths SubTestSubPaths
+}
+
+type SubTestStruct1 map[string]SubTest2Def
+type MainTestStruct struct {
+	Sub SubTestStruct1
+}
+
+func TestReplacePathForConfig(t *testing.T) {
+	config := MainTestStruct{
+		Sub: SubTestStruct1{
+			"Default": SubTest2Def{
+				Paths: SubTestSubPaths{
+					Template:    "../../../default.html",
+					Definitions: []PathValue{"../../../legacy.yaml"},
+				},
+			},
+		},
+	}
+
+	err := SetValue[*MainTestStruct](&config, "Sub.Default.Paths.Template", "test")
+	assert.Nil(t, err)
+
+}
diff --git a/set.go b/set.go
index fa7443a..f4face6 100644
--- a/set.go
+++ b/set.go
@@ -4,12 +4,26 @@
 package pathfinder
 
 import (
+	"bytes"
+	"encoding/gob"
 	"fmt"
 	"reflect"
 	"strconv"
 	"strings"
 )
 
+func deepCopy(src, dst interface{}) error {
+	var buf bytes.Buffer
+	enc := gob.NewEncoder(&buf)
+	dec := gob.NewDecoder(&buf)
+
+	if err := enc.Encode(src); err != nil {
+		return err
+	}
+
+	return dec.Decode(dst)
+}
+
 // SetValue sets the value of a field in a struct, given a path to the field.
 // The object must be a pointer to a struct, otherwise an error is returned.
 func SetValue[D any](obj D, keyWithDots string, newValue any) error {
@@ -17,7 +31,33 @@ func SetValue[D any](obj D, keyWithDots string, newValue any) error {
 	keySlice := strings.Split(keyWithDots, ".")
 	v := reflect.ValueOf(obj)
 
-	for _, key := range keySlice[0 : len(keySlice)-1] {
+	for keyIndex, key := range keySlice[0 : len(keySlice)-1] {
+
+		if v.Kind() == reflect.Map {
+			
+			if v.IsNil() {
+				return newInvalidPathError(keyWithDots)
+			}
+
+			currentValue := v.MapIndex(reflect.ValueOf(key)).Interface()
+			newValueCopy := reflect.New(reflect.TypeOf(currentValue)).Interface()
+			if err := deepCopy(currentValue, newValueCopy); err != nil {
+				return err
+			}
+
+			newValueCopyPtr := &newValueCopy
+			newValueCopyReflect := reflect.ValueOf(newValueCopyPtr).Elem()
+			if !newValueCopyReflect.CanAddr() {
+				return newCannotSetError("Wert ist nicht adressierbar")
+			}
+			newKey := strings.Join(keySlice[keyIndex+1:], ".")
+			return SetValue(newValueCopyPtr, newKey, newValue)
+		}
+
+		if v.Kind() == reflect.Ptr && v.Elem().Kind() == reflect.Interface {
+			v = v.Elem().Elem()
+		}
+
 		for v.Kind() != reflect.Ptr {
 			if v.Kind() == reflect.Invalid {
 				return newInvalidPathError(keyWithDots)
@@ -35,13 +75,13 @@ func SetValue[D any](obj D, keyWithDots string, newValue any) error {
 			return newUnsupportedTypePathError(keyWithDots, v.Type())
 		}
 
-		elem := v.Elem()
-		if elem.Kind() != reflect.Struct {
+		switch v.Elem().Kind() {
+		case reflect.Struct:
+			v = v.Elem().FieldByName(key)
+		default:
 			return newUnsupportedTypePathError(keyWithDots, v.Type())
 		}
 
-		v = elem.FieldByName(key)
-
 	}
 
 	if v.Kind() == reflect.Invalid {
-- 
GitLab