structure: check for nested structs, closes #6
This commit is contained in:
parent
cda09b4aa4
commit
4d9abb0de1
51
structure.go
51
structure.go
@ -26,6 +26,16 @@ func Map(s interface{}) map[string]interface{} {
|
|||||||
|
|
||||||
for i, field := range fields {
|
for i, field := range fields {
|
||||||
name := field.Name
|
name := field.Name
|
||||||
|
val := v.Field(i)
|
||||||
|
|
||||||
|
var finalVal interface{}
|
||||||
|
if val.Kind() == reflect.Struct {
|
||||||
|
// look out for embedded structs, and convert them to a
|
||||||
|
// map[string]interface{} too
|
||||||
|
finalVal = Map(val.Interface())
|
||||||
|
} else {
|
||||||
|
finalVal = val.Interface()
|
||||||
|
}
|
||||||
|
|
||||||
// override if the user passed a structure tag value
|
// override if the user passed a structure tag value
|
||||||
// ignore if the user passed the "-" value
|
// ignore if the user passed the "-" value
|
||||||
@ -33,7 +43,7 @@ func Map(s interface{}) map[string]interface{} {
|
|||||||
name = tag
|
name = tag
|
||||||
}
|
}
|
||||||
|
|
||||||
out[name] = v.Field(i).Interface()
|
out[name] = finalVal
|
||||||
}
|
}
|
||||||
|
|
||||||
return out
|
return out
|
||||||
@ -51,9 +61,18 @@ func Map(s interface{}) map[string]interface{} {
|
|||||||
func Values(s interface{}) []interface{} {
|
func Values(s interface{}) []interface{} {
|
||||||
v, fields := strctInfo(s)
|
v, fields := strctInfo(s)
|
||||||
|
|
||||||
t := make([]interface{}, len(fields))
|
t := make([]interface{}, 0)
|
||||||
for i := range fields {
|
for i := range fields {
|
||||||
t[i] = v.Field(i).Interface()
|
val := v.Field(i)
|
||||||
|
if val.Kind() == reflect.Struct {
|
||||||
|
// look out for embedded structs, and convert them to a
|
||||||
|
// []interface{} to be added to the final values slice
|
||||||
|
for _, embeddedVal := range Values(val.Interface()) {
|
||||||
|
t = append(t, embeddedVal)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
t = append(t, val.Interface())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return t
|
return t
|
||||||
@ -73,6 +92,16 @@ func IsValid(s interface{}) bool {
|
|||||||
v, fields := strctInfo(s)
|
v, fields := strctInfo(s)
|
||||||
|
|
||||||
for i := range fields {
|
for i := range fields {
|
||||||
|
val := v.Field(i)
|
||||||
|
if val.Kind() == reflect.Struct {
|
||||||
|
ok := IsValid(val.Interface())
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
// zero value of the given field, such as "" for string, 0 for int
|
// zero value of the given field, such as "" for string, 0 for int
|
||||||
zero := reflect.Zero(v.Field(i).Type()).Interface()
|
zero := reflect.Zero(v.Field(i).Type()).Interface()
|
||||||
|
|
||||||
@ -96,11 +125,21 @@ func IsValid(s interface{}) bool {
|
|||||||
// Note that only exported fields of a struct can be accessed, non exported
|
// Note that only exported fields of a struct can be accessed, non exported
|
||||||
// fields will be neglected. It panics if s's kind is not struct.
|
// fields will be neglected. It panics if s's kind is not struct.
|
||||||
func Fields(s interface{}) []string {
|
func Fields(s interface{}) []string {
|
||||||
_, fields := strctInfo(s)
|
v, fields := strctInfo(s)
|
||||||
|
|
||||||
keys := make([]string, len(fields))
|
keys := make([]string, 0)
|
||||||
for i, field := range fields {
|
for i, field := range fields {
|
||||||
keys[i] = field.Name
|
|
||||||
|
val := v.Field(i)
|
||||||
|
if val.Kind() == reflect.Struct {
|
||||||
|
// look out for embedded structs, and convert them to a
|
||||||
|
// []string to be added to the final values slice
|
||||||
|
for _, embeddedVal := range Fields(val.Interface()) {
|
||||||
|
keys = append(keys, embeddedVal)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
keys = append(keys, field.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
return keys
|
return keys
|
||||||
|
|||||||
@ -89,6 +89,33 @@ func TestMap_Tag(t *testing.T) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMap_Embedded(t *testing.T) {
|
||||||
|
type A struct {
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
a := A{Name: "example"}
|
||||||
|
|
||||||
|
type B struct {
|
||||||
|
A A
|
||||||
|
}
|
||||||
|
b := &B{A: a}
|
||||||
|
|
||||||
|
m := Map(b)
|
||||||
|
|
||||||
|
if typ := reflect.TypeOf(m).Kind(); typ != reflect.Map {
|
||||||
|
t.Errorf("Map should return a map type, got: %v", typ)
|
||||||
|
}
|
||||||
|
|
||||||
|
in, ok := m["A"].(map[string]interface{})
|
||||||
|
if !ok {
|
||||||
|
t.Error("Embedded structs is not available in the map")
|
||||||
|
}
|
||||||
|
|
||||||
|
if name := in["Name"].(string); name != "example" {
|
||||||
|
t.Error("Embedded A struct's Name field should give example, got: %s", name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestStruct(t *testing.T) {
|
func TestStruct(t *testing.T) {
|
||||||
var T = struct{}{}
|
var T = struct{}{}
|
||||||
|
|
||||||
@ -135,6 +162,36 @@ func TestValues(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestValues_Embedded(t *testing.T) {
|
||||||
|
type A struct {
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
a := A{Name: "example"}
|
||||||
|
|
||||||
|
type B struct {
|
||||||
|
A A
|
||||||
|
C int
|
||||||
|
}
|
||||||
|
b := &B{A: a, C: 123}
|
||||||
|
|
||||||
|
s := Values(b)
|
||||||
|
|
||||||
|
inSlice := func(val interface{}) bool {
|
||||||
|
for _, v := range s {
|
||||||
|
if reflect.DeepEqual(v, val) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, val := range []interface{}{"example", 123} {
|
||||||
|
if !inSlice(val) {
|
||||||
|
t.Errorf("Values should have the value %v", val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestFields(t *testing.T) {
|
func TestFields(t *testing.T) {
|
||||||
var T = struct {
|
var T = struct {
|
||||||
A string
|
A string
|
||||||
@ -168,6 +225,36 @@ func TestFields(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestFields_Embedded(t *testing.T) {
|
||||||
|
type A struct {
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
a := A{Name: "example"}
|
||||||
|
|
||||||
|
type B struct {
|
||||||
|
A A
|
||||||
|
C int
|
||||||
|
}
|
||||||
|
b := &B{A: a, C: 123}
|
||||||
|
|
||||||
|
s := Fields(b)
|
||||||
|
|
||||||
|
inSlice := func(val interface{}) bool {
|
||||||
|
for _, v := range s {
|
||||||
|
if reflect.DeepEqual(v, val) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, val := range []interface{}{"Name", "A", "C"} {
|
||||||
|
if !inSlice(val) {
|
||||||
|
t.Errorf("Fields should have the value %v", val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestIsValid(t *testing.T) {
|
func TestIsValid(t *testing.T) {
|
||||||
var T = struct {
|
var T = struct {
|
||||||
A string
|
A string
|
||||||
@ -197,6 +284,26 @@ func TestIsValid(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestIsValid_Embedded(t *testing.T) {
|
||||||
|
type A struct {
|
||||||
|
Name string
|
||||||
|
D string
|
||||||
|
}
|
||||||
|
a := A{Name: "example"}
|
||||||
|
|
||||||
|
type B struct {
|
||||||
|
A A
|
||||||
|
C int
|
||||||
|
}
|
||||||
|
b := &B{A: a, C: 123}
|
||||||
|
|
||||||
|
ok := IsValid(b)
|
||||||
|
if ok {
|
||||||
|
t.Error("IsValid should return false because D is not initialized")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
func TestName(t *testing.T) {
|
func TestName(t *testing.T) {
|
||||||
type Foo struct {
|
type Foo struct {
|
||||||
A string
|
A string
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user