279 lines
6.2 KiB
Go
279 lines
6.2 KiB
Go
|
package pbservice
|
||
|
|
||
|
import (
|
||
|
fmt "fmt"
|
||
|
"reflect"
|
||
|
|
||
|
types "github.com/gogo/protobuf/types"
|
||
|
)
|
||
|
|
||
|
// ProtobufTypesStructToMapStringInterface converts a protobuf/types.Struct into a
|
||
|
// map[string]interface{}.
|
||
|
func ProtobufTypesStructToMapStringInterface(s *types.Struct) map[string]interface{} {
|
||
|
if s == nil {
|
||
|
return nil
|
||
|
}
|
||
|
m := make(map[string]interface{}, len(s.Fields))
|
||
|
for k, v := range s.Fields {
|
||
|
m[k] = interfaceFromPBValue(v)
|
||
|
}
|
||
|
return m
|
||
|
}
|
||
|
|
||
|
// interfaceFromPBValue converts a protobuf Value into an interface{}
|
||
|
func interfaceFromPBValue(v *types.Value) interface{} {
|
||
|
if v == nil {
|
||
|
return nil
|
||
|
}
|
||
|
switch k := v.Kind.(type) {
|
||
|
case *types.Value_NullValue:
|
||
|
return nil
|
||
|
case *types.Value_NumberValue:
|
||
|
return k.NumberValue
|
||
|
case *types.Value_StringValue:
|
||
|
return k.StringValue
|
||
|
case *types.Value_BoolValue:
|
||
|
return k.BoolValue
|
||
|
case *types.Value_StructValue:
|
||
|
return ProtobufTypesStructToMapStringInterface(k.StructValue)
|
||
|
case *types.Value_ListValue:
|
||
|
s := make([]interface{}, len(k.ListValue.Values))
|
||
|
for i, e := range k.ListValue.Values {
|
||
|
s[i] = interfaceFromPBValue(e)
|
||
|
}
|
||
|
return s
|
||
|
default:
|
||
|
panic("unknown kind")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// MapStringInterfaceToProtobufTypesStruct converts a map[string]interface{} into a proto.Struct
|
||
|
func MapStringInterfaceToProtobufTypesStruct(m map[string]interface{}) *types.Struct {
|
||
|
if len(m) == 0 {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
fields := make(map[string]*types.Value, len(m))
|
||
|
for k, v := range m {
|
||
|
fields[k] = interfaceToPBValue(v)
|
||
|
}
|
||
|
return &types.Struct{Fields: fields}
|
||
|
}
|
||
|
|
||
|
// SliceToPBListValue converts a []interface{} into a proto.ListValue. It's used
|
||
|
// internally by MapStringInterfaceToProtobufTypesStruct when it encouters slices.
|
||
|
func SliceToPBListValue(s []interface{}) *types.ListValue {
|
||
|
if len(s) == 0 {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
vals := make([]*types.Value, len(s))
|
||
|
for i, v := range s {
|
||
|
vals[i] = interfaceToPBValue(v)
|
||
|
}
|
||
|
return &types.ListValue{Values: vals}
|
||
|
}
|
||
|
|
||
|
// interfaceToPBValue converts a interface{} into a proto.Value. It attempts to
|
||
|
// do so by type switch and simple casts where possible but falls back to
|
||
|
// reflection if necessary.
|
||
|
func interfaceToPBValue(v interface{}) *types.Value {
|
||
|
switch v := v.(type) {
|
||
|
case nil:
|
||
|
return nil
|
||
|
case int:
|
||
|
return &types.Value{
|
||
|
Kind: &types.Value_NumberValue{
|
||
|
NumberValue: float64(v),
|
||
|
},
|
||
|
}
|
||
|
case int8:
|
||
|
return &types.Value{
|
||
|
Kind: &types.Value_NumberValue{
|
||
|
NumberValue: float64(v),
|
||
|
},
|
||
|
}
|
||
|
case int32:
|
||
|
return &types.Value{
|
||
|
Kind: &types.Value_NumberValue{
|
||
|
NumberValue: float64(v),
|
||
|
},
|
||
|
}
|
||
|
case int64:
|
||
|
return &types.Value{
|
||
|
Kind: &types.Value_NumberValue{
|
||
|
NumberValue: float64(v),
|
||
|
},
|
||
|
}
|
||
|
case uint:
|
||
|
return &types.Value{
|
||
|
Kind: &types.Value_NumberValue{
|
||
|
NumberValue: float64(v),
|
||
|
},
|
||
|
}
|
||
|
case uint8:
|
||
|
return &types.Value{
|
||
|
Kind: &types.Value_NumberValue{
|
||
|
NumberValue: float64(v),
|
||
|
},
|
||
|
}
|
||
|
case uint32:
|
||
|
return &types.Value{
|
||
|
Kind: &types.Value_NumberValue{
|
||
|
NumberValue: float64(v),
|
||
|
},
|
||
|
}
|
||
|
case uint64:
|
||
|
return &types.Value{
|
||
|
Kind: &types.Value_NumberValue{
|
||
|
NumberValue: float64(v),
|
||
|
},
|
||
|
}
|
||
|
case float32:
|
||
|
return &types.Value{
|
||
|
Kind: &types.Value_NumberValue{
|
||
|
NumberValue: float64(v),
|
||
|
},
|
||
|
}
|
||
|
case float64:
|
||
|
return &types.Value{
|
||
|
Kind: &types.Value_NumberValue{
|
||
|
NumberValue: v,
|
||
|
},
|
||
|
}
|
||
|
case string:
|
||
|
return &types.Value{
|
||
|
Kind: &types.Value_StringValue{
|
||
|
StringValue: v,
|
||
|
},
|
||
|
}
|
||
|
case error:
|
||
|
return &types.Value{
|
||
|
Kind: &types.Value_StringValue{
|
||
|
StringValue: v.Error(),
|
||
|
},
|
||
|
}
|
||
|
case map[string]interface{}:
|
||
|
return &types.Value{
|
||
|
Kind: &types.Value_StructValue{
|
||
|
StructValue: MapStringInterfaceToProtobufTypesStruct(v),
|
||
|
},
|
||
|
}
|
||
|
case []interface{}:
|
||
|
return &types.Value{
|
||
|
Kind: &types.Value_ListValue{
|
||
|
ListValue: SliceToPBListValue(v),
|
||
|
},
|
||
|
}
|
||
|
default:
|
||
|
return interfaceToPBValueReflect(reflect.ValueOf(v))
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// interfaceToPBValueReflect converts a interface{} into a proto.Value using
|
||
|
// reflection.
|
||
|
func interfaceToPBValueReflect(v reflect.Value) *types.Value {
|
||
|
switch v.Kind() {
|
||
|
case reflect.Interface:
|
||
|
return interfaceToPBValue(v.Interface())
|
||
|
case reflect.Bool:
|
||
|
return &types.Value{
|
||
|
Kind: &types.Value_BoolValue{
|
||
|
BoolValue: v.Bool(),
|
||
|
},
|
||
|
}
|
||
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||
|
return &types.Value{
|
||
|
Kind: &types.Value_NumberValue{
|
||
|
NumberValue: float64(v.Int()),
|
||
|
},
|
||
|
}
|
||
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||
|
return &types.Value{
|
||
|
Kind: &types.Value_NumberValue{
|
||
|
NumberValue: float64(v.Uint()),
|
||
|
},
|
||
|
}
|
||
|
case reflect.Float32, reflect.Float64:
|
||
|
return &types.Value{
|
||
|
Kind: &types.Value_NumberValue{
|
||
|
NumberValue: v.Float(),
|
||
|
},
|
||
|
}
|
||
|
case reflect.Ptr:
|
||
|
if v.IsNil() {
|
||
|
return nil
|
||
|
}
|
||
|
return interfaceToPBValueReflect(reflect.Indirect(v))
|
||
|
case reflect.Array, reflect.Slice:
|
||
|
size := v.Len()
|
||
|
if size == 0 {
|
||
|
return nil
|
||
|
}
|
||
|
values := make([]*types.Value, size)
|
||
|
for i := 0; i < size; i++ {
|
||
|
values[i] = interfaceToPBValue(v.Index(i))
|
||
|
}
|
||
|
return &types.Value{
|
||
|
Kind: &types.Value_ListValue{
|
||
|
ListValue: &types.ListValue{
|
||
|
Values: values,
|
||
|
},
|
||
|
},
|
||
|
}
|
||
|
case reflect.Struct:
|
||
|
t := v.Type()
|
||
|
size := v.NumField()
|
||
|
if size == 0 {
|
||
|
return nil
|
||
|
}
|
||
|
fields := make(map[string]*types.Value, size)
|
||
|
for i := 0; i < size; i++ {
|
||
|
name := t.Field(i).Name
|
||
|
// Only include public fields. There may be a better way with struct tags
|
||
|
// but this works for now.
|
||
|
if len(name) > 0 && 'A' <= name[0] && name[0] <= 'Z' {
|
||
|
fields[name] = interfaceToPBValue(v.Field(i))
|
||
|
}
|
||
|
}
|
||
|
if len(fields) == 0 {
|
||
|
return nil
|
||
|
}
|
||
|
return &types.Value{
|
||
|
Kind: &types.Value_StructValue{
|
||
|
StructValue: &types.Struct{
|
||
|
Fields: fields,
|
||
|
},
|
||
|
},
|
||
|
}
|
||
|
case reflect.Map:
|
||
|
keys := v.MapKeys()
|
||
|
if len(keys) == 0 {
|
||
|
return nil
|
||
|
}
|
||
|
fields := make(map[string]*types.Value, len(keys))
|
||
|
for _, k := range keys {
|
||
|
if k.Kind() == reflect.String {
|
||
|
fields[k.String()] = interfaceToPBValue(v.MapIndex(k))
|
||
|
}
|
||
|
}
|
||
|
if len(fields) == 0 {
|
||
|
return nil
|
||
|
}
|
||
|
return &types.Value{
|
||
|
Kind: &types.Value_StructValue{
|
||
|
StructValue: &types.Struct{
|
||
|
Fields: fields,
|
||
|
},
|
||
|
},
|
||
|
}
|
||
|
default:
|
||
|
// Last resort
|
||
|
return &types.Value{
|
||
|
Kind: &types.Value_StringValue{
|
||
|
StringValue: fmt.Sprint(v),
|
||
|
},
|
||
|
}
|
||
|
}
|
||
|
}
|