structure: first commit
This commit is contained in:
parent
9916e00467
commit
ad2d6e3fe7
51
structure.go
Normal file
51
structure.go
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
// Package structure contains various utilities functions to work with structs.
|
||||||
|
package structure
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ErrNotStruct is returned when the passed value is not a struct
|
||||||
|
var ErrNotStruct = errors.New("not struct")
|
||||||
|
|
||||||
|
// ToMap converts a struct to a map[string]interface{}. The default map key
|
||||||
|
// string is the struct fieldname but this can be changed by defining a
|
||||||
|
// "structure" tag key if needed. Note that only exported fields of a struct
|
||||||
|
// can be accessed, non exported fields will be neglected.
|
||||||
|
func ToMap(in interface{}) (map[string]interface{}, error) {
|
||||||
|
out := make(map[string]interface{})
|
||||||
|
|
||||||
|
t := reflect.TypeOf(in)
|
||||||
|
|
||||||
|
// if pointer get the underlying element≤
|
||||||
|
if t.Kind() == reflect.Ptr {
|
||||||
|
t = t.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
if t.Kind() != reflect.Struct {
|
||||||
|
return nil, ErrNotStruct
|
||||||
|
}
|
||||||
|
|
||||||
|
v := reflect.ValueOf(in)
|
||||||
|
|
||||||
|
for i := 0; i < t.NumField(); i++ {
|
||||||
|
field := t.Field(i)
|
||||||
|
|
||||||
|
// we can't access the value of unexported fields
|
||||||
|
if field.PkgPath != "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
name := field.Name
|
||||||
|
|
||||||
|
// override if the user passed a structure tag
|
||||||
|
if tag := field.Tag.Get("structure"); tag != "" {
|
||||||
|
name = tag
|
||||||
|
}
|
||||||
|
|
||||||
|
out[name] = v.Field(i).Interface()
|
||||||
|
}
|
||||||
|
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
84
structure_test.go
Normal file
84
structure_test.go
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
package structure
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestToMap(t *testing.T) {
|
||||||
|
var T = struct {
|
||||||
|
A string
|
||||||
|
B int
|
||||||
|
C bool
|
||||||
|
}{
|
||||||
|
A: "a-value",
|
||||||
|
B: 2,
|
||||||
|
C: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
a, err := ToMap(T)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if typ := reflect.TypeOf(a).Kind(); typ != reflect.Map {
|
||||||
|
t.Errorf("ToMap should return a map type, got: %v", typ)
|
||||||
|
}
|
||||||
|
|
||||||
|
// we have three fields
|
||||||
|
if len(a) != 3 {
|
||||||
|
t.Errorf("ToMap should return a map of len 3, got: %d", len(a))
|
||||||
|
}
|
||||||
|
|
||||||
|
inMap := func(val interface{}) bool {
|
||||||
|
for _, v := range a {
|
||||||
|
if reflect.DeepEqual(v, val) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, val := range []interface{}{"a-value", 2, true} {
|
||||||
|
if !inMap(val) {
|
||||||
|
t.Errorf("ToMap should have the value %v", val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestToMap_Tag(t *testing.T) {
|
||||||
|
var T = struct {
|
||||||
|
A string `structure:"x"`
|
||||||
|
B int `structure:"y"`
|
||||||
|
C bool `structure:"z"`
|
||||||
|
}{
|
||||||
|
A: "a-value",
|
||||||
|
B: 2,
|
||||||
|
C: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
a, err := ToMap(T)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
inMap := func(key interface{}) bool {
|
||||||
|
for k := range a {
|
||||||
|
if reflect.DeepEqual(k, key) {
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, key := range []string{"x", "y", "z"} {
|
||||||
|
if !inMap(key) {
|
||||||
|
t.Errorf("ToMap should have the key %v", key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user