Skip to content
Snippets Groups Projects
Verified Commit ed7f99b5 authored by Volker Schukai's avatar Volker Schukai :alien:
Browse files

refactor functions moved to a separate repos

parent ac44b442
Branches
Tags
No related merge requests found
......@@ -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
)
......@@ -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)
}
......
......@@ -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) {
......
// 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
}
// 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")
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment