mirror of https://github.com/hashicorp/consul
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
278 lines
6.2 KiB
278 lines
6.2 KiB
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), |
|
}, |
|
} |
|
} |
|
}
|
|
|