572 lines
15 KiB
Go
572 lines
15 KiB
Go
|
package tftypes
|
||
|
|
||
|
import (
|
||
|
"bytes"
|
||
|
"fmt"
|
||
|
"math"
|
||
|
"math/big"
|
||
|
"sort"
|
||
|
|
||
|
msgpack "github.com/vmihailenco/msgpack/v4"
|
||
|
msgpackCodes "github.com/vmihailenco/msgpack/v4/codes"
|
||
|
)
|
||
|
|
||
|
type msgPackUnknownType struct{}
|
||
|
|
||
|
var msgPackUnknownVal = msgPackUnknownType{}
|
||
|
|
||
|
func (u msgPackUnknownType) MarshalMsgpack() ([]byte, error) {
|
||
|
return []byte{0xd4, 0, 0}, nil
|
||
|
}
|
||
|
|
||
|
// ValueFromMsgPack returns a Value from the MsgPack-encoded bytes, using the
|
||
|
// provided Type to determine what shape the Value should be.
|
||
|
// DynamicPseudoTypes will be transparently parsed into the types they
|
||
|
// represent.
|
||
|
//
|
||
|
// Deprecated: this function is exported for internal use in
|
||
|
// terraform-plugin-go. Third parties should not use it, and its behavior is
|
||
|
// not covered under the API compatibility guarantees. Don't use this.
|
||
|
func ValueFromMsgPack(data []byte, typ Type) (Value, error) {
|
||
|
r := bytes.NewReader(data)
|
||
|
dec := msgpack.NewDecoder(r)
|
||
|
return msgpackUnmarshal(dec, typ, NewAttributePath())
|
||
|
}
|
||
|
|
||
|
func msgpackUnmarshal(dec *msgpack.Decoder, typ Type, path *AttributePath) (Value, error) {
|
||
|
peek, err := dec.PeekCode()
|
||
|
if err != nil {
|
||
|
return Value{}, path.NewErrorf("error peeking next byte: %w", err)
|
||
|
}
|
||
|
if msgpackCodes.IsExt(peek) {
|
||
|
// as with go-cty, assume all extensions are unknown values
|
||
|
err := dec.Skip()
|
||
|
if err != nil {
|
||
|
return Value{}, path.NewErrorf("error skipping extension byte: %w", err)
|
||
|
}
|
||
|
return NewValue(typ, UnknownValue), nil
|
||
|
}
|
||
|
if typ.Is(DynamicPseudoType) {
|
||
|
return msgpackUnmarshalDynamic(dec, path)
|
||
|
}
|
||
|
if peek == msgpackCodes.Nil {
|
||
|
err := dec.Skip()
|
||
|
if err != nil {
|
||
|
return Value{}, path.NewErrorf("error skipping nil byte: %w", err)
|
||
|
}
|
||
|
return NewValue(typ, nil), nil
|
||
|
}
|
||
|
|
||
|
switch {
|
||
|
case typ.Is(String):
|
||
|
rv, err := dec.DecodeString()
|
||
|
if err != nil {
|
||
|
return Value{}, path.NewErrorf("error decoding string: %w", err)
|
||
|
}
|
||
|
return NewValue(String, rv), nil
|
||
|
case typ.Is(Number):
|
||
|
peek, err := dec.PeekCode()
|
||
|
if err != nil {
|
||
|
return Value{}, path.NewErrorf("couldn't peek number: %w", err)
|
||
|
}
|
||
|
if msgpackCodes.IsFixedNum(peek) {
|
||
|
rv, err := dec.DecodeInt64()
|
||
|
if err != nil {
|
||
|
return Value{}, path.NewErrorf("couldn't decode number as int64: %w", err)
|
||
|
}
|
||
|
return NewValue(Number, new(big.Float).SetInt64(rv)), nil
|
||
|
}
|
||
|
switch peek {
|
||
|
case msgpackCodes.Int8, msgpackCodes.Int16, msgpackCodes.Int32, msgpackCodes.Int64:
|
||
|
rv, err := dec.DecodeInt64()
|
||
|
if err != nil {
|
||
|
return Value{}, path.NewErrorf("couldn't decode number as int64: %w", err)
|
||
|
}
|
||
|
return NewValue(Number, new(big.Float).SetInt64(rv)), nil
|
||
|
case msgpackCodes.Uint8, msgpackCodes.Uint16, msgpackCodes.Uint32, msgpackCodes.Uint64:
|
||
|
rv, err := dec.DecodeUint64()
|
||
|
if err != nil {
|
||
|
return Value{}, path.NewErrorf("couldn't decode number as uint64: %w", err)
|
||
|
}
|
||
|
return NewValue(Number, new(big.Float).SetUint64(rv)), nil
|
||
|
case msgpackCodes.Float, msgpackCodes.Double:
|
||
|
rv, err := dec.DecodeFloat64()
|
||
|
if err != nil {
|
||
|
return Value{}, path.NewErrorf("couldn't decode number as float64: %w", err)
|
||
|
}
|
||
|
return NewValue(Number, big.NewFloat(rv)), nil
|
||
|
default:
|
||
|
rv, err := dec.DecodeString()
|
||
|
if err != nil {
|
||
|
return Value{}, path.NewErrorf("couldn't decode number as string: %w", err)
|
||
|
}
|
||
|
// according to
|
||
|
// https://github.com/hashicorp/go-cty/blob/85980079f637862fa8e43ddc82dd74315e2f4c85/cty/value_init.go#L49
|
||
|
// Base 10, precision 512, and rounding to nearest even
|
||
|
// is the standard way to handle numbers arriving as
|
||
|
// strings.
|
||
|
fv, _, err := big.ParseFloat(rv, 10, 512, big.ToNearestEven)
|
||
|
if err != nil {
|
||
|
return Value{}, path.NewErrorf("error parsing %q as number: %w", rv, err)
|
||
|
}
|
||
|
return NewValue(Number, fv), nil
|
||
|
}
|
||
|
case typ.Is(Bool):
|
||
|
rv, err := dec.DecodeBool()
|
||
|
if err != nil {
|
||
|
return Value{}, path.NewErrorf("couldn't decode bool: %w", err)
|
||
|
}
|
||
|
return NewValue(Bool, rv), nil
|
||
|
case typ.Is(List{}):
|
||
|
return msgpackUnmarshalList(dec, typ.(List).ElementType, path)
|
||
|
case typ.Is(Set{}):
|
||
|
return msgpackUnmarshalSet(dec, typ.(Set).ElementType, path)
|
||
|
case typ.Is(Map{}):
|
||
|
return msgpackUnmarshalMap(dec, typ.(Map).ElementType, path)
|
||
|
case typ.Is(Tuple{}):
|
||
|
return msgpackUnmarshalTuple(dec, typ.(Tuple).ElementTypes, path)
|
||
|
case typ.Is(Object{}):
|
||
|
return msgpackUnmarshalObject(dec, typ.(Object).AttributeTypes, path)
|
||
|
}
|
||
|
return Value{}, path.NewErrorf("unsupported type %s", typ.String())
|
||
|
}
|
||
|
|
||
|
func msgpackUnmarshalList(dec *msgpack.Decoder, typ Type, path *AttributePath) (Value, error) {
|
||
|
length, err := dec.DecodeArrayLen()
|
||
|
if err != nil {
|
||
|
return Value{}, path.NewErrorf("error decoding list length: %w", err)
|
||
|
}
|
||
|
|
||
|
switch {
|
||
|
case length < 0:
|
||
|
return NewValue(List{
|
||
|
ElementType: typ,
|
||
|
}, nil), nil
|
||
|
case length == 0:
|
||
|
return NewValue(List{
|
||
|
ElementType: typ,
|
||
|
}, []Value{}), nil
|
||
|
}
|
||
|
|
||
|
vals := make([]Value, 0, length)
|
||
|
for i := 0; i < length; i++ {
|
||
|
innerPath := path.WithElementKeyInt(i)
|
||
|
val, err := msgpackUnmarshal(dec, typ, innerPath)
|
||
|
if err != nil {
|
||
|
return Value{}, err
|
||
|
}
|
||
|
vals = append(vals, val)
|
||
|
}
|
||
|
|
||
|
elTyp := typ
|
||
|
if elTyp.Is(DynamicPseudoType) {
|
||
|
elTyp, err = TypeFromElements(vals)
|
||
|
if err != nil {
|
||
|
return Value{}, err
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return NewValue(List{
|
||
|
ElementType: elTyp,
|
||
|
}, vals), nil
|
||
|
}
|
||
|
|
||
|
func msgpackUnmarshalSet(dec *msgpack.Decoder, typ Type, path *AttributePath) (Value, error) {
|
||
|
length, err := dec.DecodeArrayLen()
|
||
|
if err != nil {
|
||
|
return Value{}, path.NewErrorf("error decoding set length: %w", err)
|
||
|
}
|
||
|
|
||
|
switch {
|
||
|
case length < 0:
|
||
|
return NewValue(Set{
|
||
|
ElementType: typ,
|
||
|
}, nil), nil
|
||
|
case length == 0:
|
||
|
return NewValue(Set{
|
||
|
ElementType: typ,
|
||
|
}, []Value{}), nil
|
||
|
}
|
||
|
|
||
|
vals := make([]Value, 0, length)
|
||
|
for i := 0; i < length; i++ {
|
||
|
innerPath := path.WithElementKeyInt(i)
|
||
|
val, err := msgpackUnmarshal(dec, typ, innerPath)
|
||
|
if err != nil {
|
||
|
return Value{}, err
|
||
|
}
|
||
|
vals = append(vals, val)
|
||
|
}
|
||
|
|
||
|
elTyp, err := TypeFromElements(vals)
|
||
|
if err != nil {
|
||
|
return Value{}, err
|
||
|
}
|
||
|
|
||
|
return NewValue(Set{
|
||
|
ElementType: elTyp,
|
||
|
}, vals), nil
|
||
|
}
|
||
|
|
||
|
func msgpackUnmarshalMap(dec *msgpack.Decoder, typ Type, path *AttributePath) (Value, error) {
|
||
|
length, err := dec.DecodeMapLen()
|
||
|
if err != nil {
|
||
|
return Value{}, path.NewErrorf("error decoding map length: %w", err)
|
||
|
}
|
||
|
|
||
|
switch {
|
||
|
case length < 0:
|
||
|
return NewValue(Map{
|
||
|
ElementType: typ,
|
||
|
}, nil), nil
|
||
|
case length == 0:
|
||
|
return NewValue(Map{
|
||
|
ElementType: typ,
|
||
|
}, map[string]Value{}), nil
|
||
|
}
|
||
|
|
||
|
vals := make(map[string]Value, length)
|
||
|
for i := 0; i < length; i++ {
|
||
|
key, err := dec.DecodeString()
|
||
|
if err != nil {
|
||
|
return Value{}, path.NewErrorf("error decoding map key: %w", err)
|
||
|
}
|
||
|
innerPath := path.WithElementKeyString(key)
|
||
|
val, err := msgpackUnmarshal(dec, typ, innerPath)
|
||
|
if err != nil {
|
||
|
return Value{}, err
|
||
|
}
|
||
|
vals[key] = val
|
||
|
}
|
||
|
|
||
|
return NewValue(Map{
|
||
|
ElementType: typ,
|
||
|
}, vals), nil
|
||
|
}
|
||
|
|
||
|
func msgpackUnmarshalTuple(dec *msgpack.Decoder, types []Type, path *AttributePath) (Value, error) {
|
||
|
length, err := dec.DecodeArrayLen()
|
||
|
if err != nil {
|
||
|
return Value{}, path.NewErrorf("error decoding tuple length: %w", err)
|
||
|
}
|
||
|
|
||
|
switch {
|
||
|
case length < 0:
|
||
|
return NewValue(Tuple{
|
||
|
ElementTypes: types,
|
||
|
}, nil), nil
|
||
|
case length != len(types):
|
||
|
return Value{}, path.NewErrorf("error decoding tuple; expected %d items, got %d", len(types), length)
|
||
|
}
|
||
|
|
||
|
vals := make([]Value, 0, length)
|
||
|
for i := 0; i < length; i++ {
|
||
|
innerPath := path.WithElementKeyInt(i)
|
||
|
typ := types[i]
|
||
|
val, err := msgpackUnmarshal(dec, typ, innerPath)
|
||
|
if err != nil {
|
||
|
return Value{}, err
|
||
|
}
|
||
|
vals = append(vals, val)
|
||
|
}
|
||
|
|
||
|
return NewValue(Tuple{
|
||
|
ElementTypes: types,
|
||
|
}, vals), nil
|
||
|
}
|
||
|
|
||
|
func msgpackUnmarshalObject(dec *msgpack.Decoder, types map[string]Type, path *AttributePath) (Value, error) {
|
||
|
length, err := dec.DecodeMapLen()
|
||
|
if err != nil {
|
||
|
return Value{}, path.NewErrorf("error decoding object length: %w", err)
|
||
|
}
|
||
|
|
||
|
switch {
|
||
|
case length < 0:
|
||
|
return NewValue(Object{
|
||
|
AttributeTypes: types,
|
||
|
}, nil), nil
|
||
|
case length != len(types):
|
||
|
return Value{}, path.NewErrorf("error decoding object; expected %d attributes, got %d", len(types), length)
|
||
|
}
|
||
|
|
||
|
vals := make(map[string]Value, length)
|
||
|
for i := 0; i < length; i++ {
|
||
|
key, err := dec.DecodeString()
|
||
|
if err != nil {
|
||
|
return Value{}, path.NewErrorf("error decoding object key: %w", err)
|
||
|
}
|
||
|
typ, exists := types[key]
|
||
|
if !exists {
|
||
|
return Value{}, path.NewErrorf("unknown attribute %q", key)
|
||
|
}
|
||
|
innerPath := path.WithAttributeName(key)
|
||
|
val, err := msgpackUnmarshal(dec, typ, innerPath)
|
||
|
if err != nil {
|
||
|
return Value{}, err
|
||
|
}
|
||
|
vals[key] = val
|
||
|
}
|
||
|
|
||
|
return NewValue(Object{
|
||
|
AttributeTypes: types,
|
||
|
}, vals), nil
|
||
|
}
|
||
|
|
||
|
func msgpackUnmarshalDynamic(dec *msgpack.Decoder, path *AttributePath) (Value, error) {
|
||
|
length, err := dec.DecodeArrayLen()
|
||
|
if err != nil {
|
||
|
return Value{}, path.NewErrorf("error checking length of DynamicPseudoType value: %w", err)
|
||
|
}
|
||
|
|
||
|
switch {
|
||
|
case length == -1:
|
||
|
return newValue(DynamicPseudoType, nil)
|
||
|
case length != 2:
|
||
|
return Value{}, path.NewErrorf("expected %d elements in DynamicPseudoType array, got %d", 2, length)
|
||
|
}
|
||
|
|
||
|
typeJSON, err := dec.DecodeBytes()
|
||
|
if err != nil {
|
||
|
return Value{}, path.NewErrorf("error decoding bytes: %w", err)
|
||
|
}
|
||
|
typ, err := ParseJSONType(typeJSON) //nolint:staticcheck
|
||
|
if err != nil {
|
||
|
return Value{}, path.NewErrorf("error parsing type information: %w", err)
|
||
|
}
|
||
|
return msgpackUnmarshal(dec, typ, path)
|
||
|
}
|
||
|
|
||
|
func marshalMsgPack(val Value, typ Type, p *AttributePath, enc *msgpack.Encoder) error {
|
||
|
if typ.Is(DynamicPseudoType) && !val.Type().Is(DynamicPseudoType) {
|
||
|
return marshalMsgPackDynamicPseudoType(val, typ, p, enc)
|
||
|
|
||
|
}
|
||
|
if !val.IsKnown() {
|
||
|
err := enc.Encode(msgPackUnknownVal)
|
||
|
if err != nil {
|
||
|
return p.NewErrorf("error encoding UnknownValue: %w", err)
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
if val.IsNull() {
|
||
|
err := enc.EncodeNil()
|
||
|
if err != nil {
|
||
|
return p.NewErrorf("error encoding null value: %w", err)
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
switch {
|
||
|
case typ.Is(String):
|
||
|
return marshalMsgPackString(val, typ, p, enc)
|
||
|
case typ.Is(Number):
|
||
|
return marshalMsgPackNumber(val, typ, p, enc)
|
||
|
case typ.Is(Bool):
|
||
|
return marshalMsgPackBool(val, typ, p, enc)
|
||
|
case typ.Is(List{}):
|
||
|
return marshalMsgPackList(val, typ, p, enc)
|
||
|
case typ.Is(Set{}):
|
||
|
return marshalMsgPackSet(val, typ, p, enc)
|
||
|
case typ.Is(Map{}):
|
||
|
return marshalMsgPackMap(val, typ, p, enc)
|
||
|
case typ.Is(Tuple{}):
|
||
|
return marshalMsgPackTuple(val, typ, p, enc)
|
||
|
case typ.Is(Object{}):
|
||
|
return marshalMsgPackObject(val, typ, p, enc)
|
||
|
}
|
||
|
return fmt.Errorf("unknown type %s", typ)
|
||
|
}
|
||
|
|
||
|
func marshalMsgPackDynamicPseudoType(val Value, _ Type, p *AttributePath, enc *msgpack.Encoder) error {
|
||
|
typeJSON, err := val.Type().MarshalJSON()
|
||
|
if err != nil {
|
||
|
return p.NewErrorf("error generating JSON for type %s: %w", val.Type(), err)
|
||
|
}
|
||
|
err = enc.EncodeArrayLen(2)
|
||
|
if err != nil {
|
||
|
return p.NewErrorf("error encoding array length: %w", err)
|
||
|
}
|
||
|
err = enc.EncodeBytes(typeJSON)
|
||
|
if err != nil {
|
||
|
return p.NewErrorf("error encoding JSON type info: %w", err)
|
||
|
}
|
||
|
err = marshalMsgPack(val, val.Type(), p, enc)
|
||
|
if err != nil {
|
||
|
return p.NewErrorf("error marshaling DynamicPseudoType value: %w", err)
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func marshalMsgPackString(val Value, typ Type, p *AttributePath, enc *msgpack.Encoder) error {
|
||
|
s, ok := val.value.(string)
|
||
|
if !ok {
|
||
|
return unexpectedValueTypeError(p, s, val.value, typ)
|
||
|
}
|
||
|
err := enc.EncodeString(s)
|
||
|
if err != nil {
|
||
|
return p.NewErrorf("error encoding string value: %w", err)
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func marshalMsgPackNumber(val Value, typ Type, p *AttributePath, enc *msgpack.Encoder) error {
|
||
|
n, ok := val.value.(*big.Float)
|
||
|
if !ok {
|
||
|
return unexpectedValueTypeError(p, n, val.value, typ)
|
||
|
}
|
||
|
if n.IsInf() {
|
||
|
if n.Sign() == -1 {
|
||
|
err := enc.EncodeFloat64(math.Inf(-1))
|
||
|
if err != nil {
|
||
|
return p.NewErrorf("error encoding negative infinity: %w", err)
|
||
|
}
|
||
|
} else if n.Sign() == 1 {
|
||
|
err := enc.EncodeFloat64(math.Inf(1))
|
||
|
if err != nil {
|
||
|
return p.NewErrorf("error encoding positive infinity: %w", err)
|
||
|
}
|
||
|
} else {
|
||
|
return p.NewErrorf("error encoding unknown infiniy: sign %d is unknown", n.Sign())
|
||
|
}
|
||
|
} else if iv, acc := n.Int64(); acc == big.Exact {
|
||
|
err := enc.EncodeInt(iv)
|
||
|
if err != nil {
|
||
|
return p.NewErrorf("error encoding int value: %w", err)
|
||
|
}
|
||
|
} else if fv, acc := n.Float64(); acc == big.Exact {
|
||
|
err := enc.EncodeFloat64(fv)
|
||
|
if err != nil {
|
||
|
return p.NewErrorf("error encoding float value: %w", err)
|
||
|
}
|
||
|
} else {
|
||
|
err := enc.EncodeString(n.Text('f', -1))
|
||
|
if err != nil {
|
||
|
return p.NewErrorf("error encoding number string value: %w", err)
|
||
|
}
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func marshalMsgPackBool(val Value, typ Type, p *AttributePath, enc *msgpack.Encoder) error {
|
||
|
b, ok := val.value.(bool)
|
||
|
if !ok {
|
||
|
return unexpectedValueTypeError(p, b, val.value, typ)
|
||
|
}
|
||
|
err := enc.EncodeBool(b)
|
||
|
if err != nil {
|
||
|
return p.NewErrorf("error encoding bool value: %w", err)
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func marshalMsgPackList(val Value, typ Type, p *AttributePath, enc *msgpack.Encoder) error {
|
||
|
l, ok := val.value.([]Value)
|
||
|
if !ok {
|
||
|
return unexpectedValueTypeError(p, l, val.value, typ)
|
||
|
}
|
||
|
err := enc.EncodeArrayLen(len(l))
|
||
|
if err != nil {
|
||
|
return p.NewErrorf("error encoding list length: %w", err)
|
||
|
}
|
||
|
for pos, i := range l {
|
||
|
err := marshalMsgPack(i, typ.(List).ElementType, p.WithElementKeyInt(pos), enc)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func marshalMsgPackSet(val Value, typ Type, p *AttributePath, enc *msgpack.Encoder) error {
|
||
|
s, ok := val.value.([]Value)
|
||
|
if !ok {
|
||
|
return unexpectedValueTypeError(p, s, val.value, typ)
|
||
|
}
|
||
|
err := enc.EncodeArrayLen(len(s))
|
||
|
if err != nil {
|
||
|
return p.NewErrorf("error encoding set length: %w", err)
|
||
|
}
|
||
|
for _, i := range s {
|
||
|
err := marshalMsgPack(i, typ.(Set).ElementType, p.WithElementKeyValue(i), enc)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func marshalMsgPackMap(val Value, typ Type, p *AttributePath, enc *msgpack.Encoder) error {
|
||
|
m, ok := val.value.(map[string]Value)
|
||
|
if !ok {
|
||
|
return unexpectedValueTypeError(p, m, val.value, typ)
|
||
|
}
|
||
|
err := enc.EncodeMapLen(len(m))
|
||
|
if err != nil {
|
||
|
return p.NewErrorf("error encoding map length: %w", err)
|
||
|
}
|
||
|
for k, v := range m {
|
||
|
err := marshalMsgPack(NewValue(String, k), String, p.WithElementKeyString(k), enc)
|
||
|
if err != nil {
|
||
|
return p.NewErrorf("error encoding map key: %w", err)
|
||
|
}
|
||
|
err = marshalMsgPack(v, typ.(Map).ElementType, p, enc)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func marshalMsgPackTuple(val Value, typ Type, p *AttributePath, enc *msgpack.Encoder) error {
|
||
|
t, ok := val.value.([]Value)
|
||
|
if !ok {
|
||
|
return unexpectedValueTypeError(p, t, val.value, typ)
|
||
|
}
|
||
|
types := typ.(Tuple).ElementTypes
|
||
|
err := enc.EncodeArrayLen(len(types))
|
||
|
if err != nil {
|
||
|
return p.NewErrorf("error encoding tuple length: %w", err)
|
||
|
}
|
||
|
for pos, v := range t {
|
||
|
ty := types[pos]
|
||
|
err := marshalMsgPack(v, ty, p.WithElementKeyInt(pos), enc)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func marshalMsgPackObject(val Value, typ Type, p *AttributePath, enc *msgpack.Encoder) error {
|
||
|
o, ok := val.value.(map[string]Value)
|
||
|
if !ok {
|
||
|
return unexpectedValueTypeError(p, o, val.value, typ)
|
||
|
}
|
||
|
types := typ.(Object).AttributeTypes
|
||
|
keys := make([]string, 0, len(types))
|
||
|
for k := range types {
|
||
|
keys = append(keys, k)
|
||
|
}
|
||
|
sort.Strings(keys)
|
||
|
err := enc.EncodeMapLen(len(keys))
|
||
|
if err != nil {
|
||
|
return p.NewErrorf("error encoding object length: %w", err)
|
||
|
}
|
||
|
for _, k := range keys {
|
||
|
ty := types[k]
|
||
|
v, ok := o[k]
|
||
|
if !ok {
|
||
|
return p.WithAttributeName(k).NewErrorf("no value set")
|
||
|
}
|
||
|
err := marshalMsgPack(NewValue(String, k), String, p.WithAttributeName(k), enc)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
err = marshalMsgPack(v, ty, p.WithAttributeName(k), enc)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
}
|
||
|
return nil
|
||
|
}
|