diff --git a/go.mod b/go.mod index c30c8e264bb9001b5a17c310d8a2a4ae09d69179..4cc393e3a414d80083df9df70e2b8f007ed19194 100644 --- a/go.mod +++ b/go.mod @@ -8,5 +8,6 @@ require ( github.com/agnivade/levenshtein v1.1.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + gitlab.schukai.com/oss/libraries/go/utilities/pathfinder v0.3.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 06cda3fce08e3b3d052431bb924b3a82f92686e2..f41749f6d814c2f5ecabba782362c1b9309d1734 100644 --- a/go.sum +++ b/go.sum @@ -12,6 +12,8 @@ github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSS github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +gitlab.schukai.com/oss/libraries/go/utilities/pathfinder v0.3.0 h1:mSxk2q/npskmHMmw1oF4moccjGav5dL6qmff2njUV7A= +gitlab.schukai.com/oss/libraries/go/utilities/pathfinder v0.3.0/go.mod h1:UvdD4NAf3gLKYafabJD7e9ZCOetzM9JZ9y4GkZukPVU= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/mapping.go b/mapping.go index 18fbe371fce01c8a2ea967b6b44ca40ab31ea5f8..f0e8a26b61773f4c1dbf6ec6c4cc573c49bc45f8 100644 --- a/mapping.go +++ b/mapping.go @@ -5,6 +5,7 @@ package xflags import ( "flag" + "gitlab.schukai.com/oss/libraries/go/utilities/pathfinder" "reflect" "strings" ) @@ -47,7 +48,7 @@ func (s *Settings[C]) assignValues(c cmd[C]) { pa := append(c.valuePath, k) p := strings.Join(pa, ".") - err := SetValueUsingPath(&s.definitions, p, value) + err := pathfinder.SetValue(&s.definitions, p, value) if err != nil { s.errors = append(s.errors, err) } diff --git a/mapping_test.go b/mapping_test.go index 723d955f85a9135ce2226639f1d51d08101524bc..93a3dc02b52e6adb4ee7b98c999aad4eb49a8ca9 100644 --- a/mapping_test.go +++ b/mapping_test.go @@ -5,6 +5,7 @@ package xflags import ( "github.com/stretchr/testify/assert" + "gitlab.schukai.com/oss/libraries/go/utilities/pathfinder" "testing" ) @@ -39,8 +40,8 @@ func TestFlagCopyToShadow(t *testing.T) { } func (s *ConfigStruct6) Copy(m map[string]any) { - SetValueUsingPath(s, "ValGlobal1", (m["Global1"])) - SetValueUsingPath(s, "ValCommand1Flag2", (m["Command1.Command1Flag2"])) + pathfinder.SetValue(s, "ValGlobal1", (m["Global1"])) + pathfinder.SetValue(s, "ValCommand1Flag2", (m["Command1.Command1Flag2"])) } func TestCopyable(t *testing.T) { diff --git a/pathfind.go b/pathfind.go deleted file mode 100644 index 951bf8fe5d951502bcbcdb3c1e1ab5fc169ece84..0000000000000000000000000000000000000000 --- a/pathfind.go +++ /dev/null @@ -1,179 +0,0 @@ -// Copyright 2022 schukai GmbH -// SPDX-License-Identifier: AGPL-3.0 - -package xflags - -import ( - "fmt" - "reflect" - "strconv" - "strings" -) - -// This function returns the value of a field in a struct, given a path to the field. -func GetValueFrom[D any](obj D, keyWithDots string) (any, error) { - keySlice := strings.Split(keyWithDots, ".") - v := reflect.ValueOf(obj) - - for _, key := range keySlice[0 : len(keySlice)-1] { - for v.Kind() == reflect.Ptr { - v = v.Elem() - } - - if v.Kind() != reflect.Struct { - return nil, newUnsupportedTypePathError(keyWithDots, v.Type()) - } - - v = v.FieldByName(key) - } - - if v.Kind() == reflect.Invalid { - return nil, newInvalidPathError(keyWithDots) - } - - for v.Kind() == reflect.Ptr { - v = v.Elem() - } - - // non-supporter type at the top of the path - if v.Kind() != reflect.Struct { - return nil, newUnsupportedTypeAtTopOfPathError(keyWithDots, v.Type()) - } - - v = v.FieldByName(keySlice[len(keySlice)-1]) - if !v.IsValid() { - return nil, newInvalidPathError(keyWithDots) - } - - return v.Interface(), nil - -} - -// This function sets the value of a field in a struct, given a path to the field. -func SetValueUsingPath[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 v.Kind() != reflect.Ptr { - v = v.Addr() - } - - if v.Kind() != reflect.Ptr { - return newUnsupportedTypePathError(keyWithDots, v.Type()) - } - - elem := v.Elem() - if elem.Kind() != reflect.Struct { - return newUnsupportedTypePathError(keyWithDots, v.Type()) - } - - v = elem.FieldByName(key) - - } - - if v.Kind() == reflect.Invalid { - return newInvalidPathError(keyWithDots) - } - - for v.Kind() == reflect.Ptr { - v = v.Elem() - } - - // non-supporter type at the top of the path - if v.Kind() != reflect.Struct { - return newUnsupportedTypeAtTopOfPathError(keyWithDots, v.Type()) - } - - v = v.FieldByName(keySlice[len(keySlice)-1]) - if !v.IsValid() { - return newInvalidPathError(keyWithDots) - } - - if !v.CanSet() { - return newCannotSetError(keyWithDots) - } - - switch v.Kind() { - case reflect.Ptr: - if newValue == nil { - v.Set(reflect.Zero(v.Type())) - } else { - v.Set(reflect.ValueOf(&newValue)) - } - return nil - } - - newValueType := reflect.TypeOf(newValue) - if newValueType == nil { - return newUnsupportedTypePathError(keyWithDots, v.Type()) - } - - newValueKind := reflect.TypeOf(newValue).Kind() - - switch v.Kind() { - case reflect.String: - if newValueKind == reflect.String { - v.SetString(newValue.(string)) - } else { - v.SetString(fmt.Sprintf("%v", newValue)) - } - - case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: - - if newValueKind == reflect.Int { - v.SetInt(int64(newValue.(int))) - } else { - s, err := strconv.ParseInt(newValue.(string), 10, 64) - if err != nil { - return err - } - v.SetInt(s) - } - - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - - if newValueKind == reflect.Int { - v.SetUint(uint64(newValue.(int))) - } else { - s, err := strconv.ParseInt(newValue.(string), 10, 64) - if err != nil { - return err - } - v.SetUint(uint64(s)) - } - - case reflect.Bool: - - if newValueKind == reflect.Bool { - v.SetBool(newValue.(bool)) - } else { - b, err := strconv.ParseBool(newValue.(string)) - if err != nil { - return err - } - - v.SetBool(b) - } - - case reflect.Float64, reflect.Float32: - - if newValueKind == reflect.Float64 { - v.SetFloat(newValue.(float64)) - } else { - s, err := strconv.ParseFloat(newValue.(string), 64) - if err != nil { - return err - } - - v.SetFloat(s) - } - - default: - return newInvalidTypeForPathError(keyWithDots, v.Type().String(), newValueKind.String()) - } - - return nil - -} diff --git a/pathfind_test.go b/pathfind_test.go deleted file mode 100644 index e3145206698f2fcb45af84fbf2afb277b99e274e..0000000000000000000000000000000000000000 --- a/pathfind_test.go +++ /dev/null @@ -1,278 +0,0 @@ -// Copyright 2022 schukai GmbH -// SPDX-License-Identifier: AGPL-3.0 - -package xflags - -import "testing" - -type PathfindTestStruct1 struct { - A bool - Sub1 struct { - B bool - Bi int - Bs string - Bf float64 - Sub2 struct { - C bool - Ci int - Cs string - Cf float64 - - Sub3 struct { - D bool - Di int - Ds string - Df float64 - } - } - } -} - -func TestPathFindError(t *testing.T) { - - s := PathfindTestStruct1{} - - _, err := GetValueFrom[PathfindTestStruct1](s, "Sub1.Sub2.Sub3.XX") - if err == nil { - t.Error("err == nil") - } - -} - -func TestPathFindSetValueString(t *testing.T) { - - testData := map[string]string{ - "Sub1.B": "true", - "Sub1.Bi": "2", - "Sub1.Bs": "3", - "Sub1.Bf": "4.0", - "Sub1.Sub2.C": "true", - "Sub1.Sub2.Ci": "2", - "Sub1.Sub2.Cs": "3", - "Sub1.Sub2.Cf": "4.0", - "Sub1.Sub2.Sub3.D": "true", - "Sub1.Sub2.Sub3.Di": "2", - "Sub1.Sub2.Sub3.Ds": "3", - "Sub1.Sub2.Sub3.Df": "4.0", - } - - for k, v := range testData { - s := &PathfindTestStruct1{} - err := SetValueUsingPath[*PathfindTestStruct1](s, k, v) - if err != nil { - t.Error(err) - } - } -} - -func TestPathFindGetValueFrom(t *testing.T) { - - s := PathfindTestStruct1{} - - s.Sub1.B = true - s.Sub1.Bi = 2 - s.Sub1.Bs = "3" - s.Sub1.Bf = 4.0 - - v, err := GetValueFrom[PathfindTestStruct1](s, "Sub1.B") - if err != nil { - t.Error(err) - } - - if v != true { - t.Error("v != true") - } - - v, err = GetValueFrom[PathfindTestStruct1](s, "Sub1.Bi") - if err != nil { - t.Error(err) - } - - if v != 2 { - t.Error("v != 2") - } - - v, err = GetValueFrom[PathfindTestStruct1](s, "Sub1.Bs") - if err != nil { - t.Error(err) - } - - if v != "3" { - t.Error("v != 3") - } - - v, err = GetValueFrom[PathfindTestStruct1](s, "Sub1.Bf") - if err != nil { - t.Error(err) - } - - if v != 4.0 { - t.Error("v != 4.0") - } - - s.Sub1.Sub2.C = true - s.Sub1.Sub2.Ci = 2 - s.Sub1.Sub2.Cs = "3" - s.Sub1.Sub2.Cf = 4.0 - - v, err = GetValueFrom[PathfindTestStruct1](s, "Sub1.Sub2.C") - if err != nil { - t.Error(err) - } - - if v != true { - t.Error("v != true") - } - - v, err = GetValueFrom[PathfindTestStruct1](s, "Sub1.Sub2.Ci") - if err != nil { - t.Error(err) - } - - if v != 2 { - t.Error("v != 2") - } - - v, err = GetValueFrom[PathfindTestStruct1](s, "Sub1.Sub2.Cs") - if err != nil { - t.Error(err) - } - - if v != "3" { - t.Error("v != 3") - } - - v, err = GetValueFrom[PathfindTestStruct1](s, "Sub1.Sub2.Cf") - if err != nil { - t.Error(err) - } - - if v != 4.0 { - t.Error("v != 4.0") - } - - s.Sub1.Sub2.Sub3.D = true - s.Sub1.Sub2.Sub3.Di = 2 - s.Sub1.Sub2.Sub3.Ds = "3" - s.Sub1.Sub2.Sub3.Df = 4.0 - - v, err = GetValueFrom[PathfindTestStruct1](s, "Sub1.Sub2.Sub3.D") - if err != nil { - t.Error(err) - - } - - if v != true { - t.Error("v != true") - } - - v, err = GetValueFrom[PathfindTestStruct1](s, "Sub1.Sub2.Sub3.Di") - if err != nil { - t.Error(err) - } - - if v != 2 { - t.Error("v != 2") - } - - v, err = GetValueFrom[PathfindTestStruct1](s, "Sub1.Sub2.Sub3.Ds") - if err != nil { - t.Error(err) - } - - if v != "3" { - t.Error("v != 3") - } - - v, err = GetValueFrom[PathfindTestStruct1](s, "Sub1.Sub2.Sub3.Df") - if err != nil { - t.Error(err) - } - - if v != 4.0 { - t.Error("v != 4.0") - } - -} - -func TestPathFindSetValueFrom(t *testing.T) { - s := &PathfindTestStruct1{} - - SetValueUsingPath[*PathfindTestStruct1](s, "Sub1.B", "true") - SetValueUsingPath[*PathfindTestStruct1](s, "Sub1.Bi", "2") - SetValueUsingPath[*PathfindTestStruct1](s, "Sub1.Bs", "3") - SetValueUsingPath[*PathfindTestStruct1](s, "Sub1.Bf", "4.0") - - if s.Sub1.B != true { - t.Error("s.Sub1.B != true") - } - - if s.Sub1.Bi != 2 { - t.Error("s.Sub1.Bi != 2") - - } - - if s.Sub1.Bs != "3" { - t.Error("s.Sub1.Bs != 3") - } - - if s.Sub1.Bf != 4.0 { - t.Error("s.Sub1.Bf != 4.0") - } - - SetValueUsingPath[*PathfindTestStruct1](s, "Sub1.Sub2.C", "true") - SetValueUsingPath[*PathfindTestStruct1](s, "Sub1.Sub2.Ci", "2") - SetValueUsingPath[*PathfindTestStruct1](s, "Sub1.Sub2.Cs", "3") - SetValueUsingPath[*PathfindTestStruct1](s, "Sub1.Sub2.Cf", "4.0") - - if s.Sub1.Sub2.C != true { - t.Error("s.Sub1.Sub2.C != true") - - } - - if s.Sub1.Sub2.Ci != 2 { - t.Error("s.Sub1.Sub2.Ci != 2") - - } - - if s.Sub1.Sub2.Cs != "3" { - t.Error("s.Sub1.Sub2.Cs != 3") - - } - - if s.Sub1.Sub2.Cf != 4.0 { - t.Error("s.Sub1.Sub2.Cf != 4.0") - - } - - if s.Sub1.Sub2.Sub3.D != false { - t.Error("s.Sub1.Sub2.Sub3.D != false") - - } - - SetValueUsingPath[*PathfindTestStruct1](s, "Sub1.Sub2.Sub3.D", "true") - SetValueUsingPath[*PathfindTestStruct1](s, "Sub1.Sub2.Sub3.Di", "2") - SetValueUsingPath[*PathfindTestStruct1](s, "Sub1.Sub2.Sub3.Ds", "3") - SetValueUsingPath[*PathfindTestStruct1](s, "Sub1.Sub2.Sub3.Df", "4.0") - - if s.Sub1.Sub2.Sub3.D != true { - t.Error("s.Sub1.Sub2.Sub3.D != true") - - } - - if s.Sub1.Sub2.Sub3.Di != 2 { - t.Error("s.Sub1.Sub2.Sub3.Di != 2") - - } - - if s.Sub1.Sub2.Sub3.Ds != "3" { - t.Error("s.Sub1.Sub2.Sub3.Ds != 3") - - } - - if s.Sub1.Sub2.Sub3.Df != 4.0 { - t.Error("s.Sub1.Sub2.Sub3.Df != 4.0") - - } - -}