Skip to content

Commit

Permalink
Add EscapePropertyName and UnescapePropertyName functions to escape d…
Browse files Browse the repository at this point in the history
…ots in property names. Update GetFieldValueFromInterface and GetNestedFieldValueFromInterface to use UnescapePropertyName to support names with dots. Fixes #21. Closes #23
  • Loading branch information
kaidaguerre committed Jun 21, 2022
1 parent af05364 commit 8e2a216
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 38 deletions.
38 changes: 0 additions & 38 deletions helpers/reflect.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,46 +5,8 @@ import (
"reflect"
"regexp"
"strconv"
"strings"

"github.com/tkrajina/go-reflector/reflector"
)

// GetFieldValueFromInterface uses reflection to return the value of the given property path
func GetFieldValueFromInterface(i interface{}, fieldName string) (interface{}, bool) {
obj := reflector.New(i)
if arrayProperty, index, ok := IsFieldArray(fieldName); ok {
if arrVal, ok := GetFieldValueFromInterface(i, arrayProperty); ok {
return GetArrayValue(arrVal, index)
}
return nil, false
}

if reflect.TypeOf(i).Kind() == reflect.Map {
return obj.GetByKey(fieldName)
}

val, err := obj.Field(fieldName).Get()
return val, err == nil
}

// GetNestedFieldValueFromInterface uses reflection to return the value of the given nested property path
func GetNestedFieldValueFromInterface(item interface{}, propertyPath string) (interface{}, bool) {
var value interface{}
var ok bool
parent := item
var pathSegments = strings.Split(propertyPath, ".")
for _, propertyName := range pathSegments {
value, ok = GetFieldValueFromInterface(parent, propertyName)
if !ok {
return nil, false
}
// update parent for next iteration
parent = value
}
return value, true
}

func GetArrayValue(i interface{}, index int) (interface{}, bool) {
if reflect.TypeOf(i).Kind() != reflect.Slice {
return nil, false
Expand Down
61 changes: 61 additions & 0 deletions helpers/reflect_field_value.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package helpers

import (
"reflect"
"strings"

"github.com/tkrajina/go-reflector/reflector"
)

// GetFieldValueFromInterface uses reflection to return the value of the given property path
func GetFieldValueFromInterface(i interface{}, fieldName string) (interface{}, bool) {
// unescape property name
fieldName = UnescapePropertyName(fieldName)
obj := reflector.New(i)
if arrayProperty, index, ok := IsFieldArray(fieldName); ok {
if arrVal, ok := GetFieldValueFromInterface(i, arrayProperty); ok {
return GetArrayValue(arrVal, index)
}
return nil, false
}

if reflect.TypeOf(i).Kind() == reflect.Map {
return obj.GetByKey(fieldName)
}

val, err := obj.Field(fieldName).Get()
return val, err == nil
}

// GetNestedFieldValueFromInterface uses reflection to return the value of the given nested property path
func GetNestedFieldValueFromInterface(item interface{}, propertyPath string) (interface{}, bool) {
var value interface{}
var ok bool
parent := item
var pathSegments = strings.Split(propertyPath, ".")
for _, fieldName := range pathSegments {
// if there are any dots encoded in this segment, decode
fieldName = UnescapePropertyName(fieldName)
value, ok = GetFieldValueFromInterface(parent, fieldName)
if !ok {
return nil, false
}
// update parent for next iteration
parent = value
}
return value, true
}

const propertyPathDotEscape = "$steampipe_escaped_dot$"

// helpers to support property names containing a dot

// EscapePropertyName replaces any '.' characters in the property name with propertyPathDotEscape
func EscapePropertyName(name string) string {
return strings.Replace(name, ".", propertyPathDotEscape, -1)
}

// UnescapePropertyName replaces any propertyPathDotEscape occurrences with "."
func UnescapePropertyName(name string) string {
return strings.Replace(name, propertyPathDotEscape, ".", -1)
}
10 changes: 10 additions & 0 deletions helpers/reflect_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,11 @@ var testCasesGetNestedFieldValueFromInterface = map[string]GetNestedFieldValueFr
fieldName: "Str.S",
expected: "foo S2",
},
"struct double level string escaped": {
input: s1Instance,
fieldName: fmt.Sprintf("%s.%s", EscapePropertyName("Str"), EscapePropertyName("S")),
expected: "foo S2",
},
"Array property path: Nested multi-dimensional failure": {
input: s1Instance,
fieldName: "Str.Arr[0][3]",
Expand Down Expand Up @@ -186,6 +191,11 @@ var testCasesGetNestedFieldValueFromInterface = map[string]GetNestedFieldValueFr
fieldName: "I",
expected: 1,
},
"struct single level int (escaped)": {
input: s1Instance,
fieldName: EscapePropertyName("I"),
expected: 1,
},
"struct pointer single level int": {
input: &s1Instance,
fieldName: "I",
Expand Down

0 comments on commit 8e2a216

Please sign in to comment.