8 "github.com/vmihailenco/msgpack"
9 "github.com/zclconf/go-cty/cty"
10 "github.com/zclconf/go-cty/cty/convert"
13 // Marshal produces a msgpack serialization of the given value that
14 // can be decoded into the given type later using Unmarshal.
16 // The given value must conform to the given type, or an error will
18 func Marshal(val cty.Value, ty cty.Type) ([]byte, error) {
19 errs := val.Type().TestConformance(ty)
21 // Attempt a conversion
23 val, err = convert.Convert(val, ty)
29 // From this point onward, val can be assumed to be conforming to t.
33 enc := msgpack.NewEncoder(&buf)
35 err := marshal(val, ty, path, enc)
40 return buf.Bytes(), nil
43 func marshal(val cty.Value, ty cty.Type, path cty.Path, enc *msgpack.Encoder) error {
44 // If we're going to decode as DynamicPseudoType then we need to save
45 // dynamic type information to recover the real type.
46 if ty == cty.DynamicPseudoType && val.Type() != cty.DynamicPseudoType {
47 return marshalDynamic(val, path, enc)
51 err := enc.Encode(unknownVal)
53 return path.NewError(err)
58 err := enc.EncodeNil()
60 return path.NewError(err)
65 // The caller should've guaranteed that the given val is conformant with
66 // the given type ty, so we'll proceed under that assumption here.
68 case ty.IsPrimitiveType():
71 err := enc.EncodeString(val.AsString())
73 return path.NewError(err)
79 case val.RawEquals(cty.PositiveInfinity):
80 err = enc.EncodeFloat64(positiveInfinity)
81 case val.RawEquals(cty.NegativeInfinity):
82 err = enc.EncodeFloat64(negativeInfinity)
84 bf := val.AsBigFloat()
85 if iv, acc := bf.Int64(); acc == big.Exact {
86 err = enc.EncodeInt(iv)
87 } else if fv, acc := bf.Float64(); acc == big.Exact {
88 err = enc.EncodeFloat64(fv)
90 err = enc.EncodeString(bf.Text('f', -1))
94 return path.NewError(err)
98 err := enc.EncodeBool(val.True())
100 return path.NewError(err)
104 panic("unsupported primitive type")
106 case ty.IsListType(), ty.IsSetType():
107 enc.EncodeArrayLen(val.LengthInt())
108 ety := ty.ElementType()
109 it := val.ElementIterator()
110 path := append(path, nil) // local override of 'path' with extra element
112 ek, ev := it.Element()
113 path[len(path)-1] = cty.IndexStep{
116 err := marshal(ev, ety, path, enc)
123 enc.EncodeMapLen(val.LengthInt())
124 ety := ty.ElementType()
125 it := val.ElementIterator()
126 path := append(path, nil) // local override of 'path' with extra element
128 ek, ev := it.Element()
129 path[len(path)-1] = cty.IndexStep{
133 err = marshal(ek, ek.Type(), path, enc)
137 err = marshal(ev, ety, path, enc)
143 case ty.IsTupleType():
144 etys := ty.TupleElementTypes()
145 it := val.ElementIterator()
146 path := append(path, nil) // local override of 'path' with extra element
148 enc.EncodeArrayLen(len(etys))
151 ek, ev := it.Element()
152 path[len(path)-1] = cty.IndexStep{
155 err := marshal(ev, ety, path, enc)
162 case ty.IsObjectType():
163 atys := ty.AttributeTypes()
164 path := append(path, nil) // local override of 'path' with extra element
166 names := make([]string, 0, len(atys))
167 for k := range atys {
168 names = append(names, k)
172 enc.EncodeMapLen(len(names))
174 for _, k := range names {
177 path[len(path)-1] = cty.GetAttrStep{
181 err = marshal(cty.StringVal(k), cty.String, path, enc)
185 err = marshal(av, aty, path, enc)
191 case ty.IsCapsuleType():
192 return path.NewErrorf("capsule types not supported for msgpack encoding")
194 // should never happen
195 return path.NewErrorf("cannot msgpack-serialize %s", ty.FriendlyName())
199 // marshalDynamic adds an extra wrapping object containing dynamic type
200 // information for the given value.
201 func marshalDynamic(val cty.Value, path cty.Path, enc *msgpack.Encoder) error {
206 return enc.Encode(&dv)