]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blob - vendor/github.com/zclconf/go-cty/cty/msgpack/marshal.go
Upgrade to 0.12
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / zclconf / go-cty / cty / msgpack / marshal.go
1 package msgpack
2
3 import (
4 "bytes"
5 "math/big"
6 "sort"
7
8 "github.com/vmihailenco/msgpack"
9 "github.com/zclconf/go-cty/cty"
10 "github.com/zclconf/go-cty/cty/convert"
11 )
12
13 // Marshal produces a msgpack serialization of the given value that
14 // can be decoded into the given type later using Unmarshal.
15 //
16 // The given value must conform to the given type, or an error will
17 // be returned.
18 func Marshal(val cty.Value, ty cty.Type) ([]byte, error) {
19 errs := val.Type().TestConformance(ty)
20 if errs != nil {
21 // Attempt a conversion
22 var err error
23 val, err = convert.Convert(val, ty)
24 if err != nil {
25 return nil, err
26 }
27 }
28
29 // From this point onward, val can be assumed to be conforming to t.
30
31 var path cty.Path
32 var buf bytes.Buffer
33 enc := msgpack.NewEncoder(&buf)
34
35 err := marshal(val, ty, path, enc)
36 if err != nil {
37 return nil, err
38 }
39
40 return buf.Bytes(), nil
41 }
42
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)
48 }
49
50 if !val.IsKnown() {
51 err := enc.Encode(unknownVal)
52 if err != nil {
53 return path.NewError(err)
54 }
55 return nil
56 }
57 if val.IsNull() {
58 err := enc.EncodeNil()
59 if err != nil {
60 return path.NewError(err)
61 }
62 return nil
63 }
64
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.
67 switch {
68 case ty.IsPrimitiveType():
69 switch ty {
70 case cty.String:
71 err := enc.EncodeString(val.AsString())
72 if err != nil {
73 return path.NewError(err)
74 }
75 return nil
76 case cty.Number:
77 var err error
78 switch {
79 case val.RawEquals(cty.PositiveInfinity):
80 err = enc.EncodeFloat64(positiveInfinity)
81 case val.RawEquals(cty.NegativeInfinity):
82 err = enc.EncodeFloat64(negativeInfinity)
83 default:
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)
89 } else {
90 err = enc.EncodeString(bf.Text('f', -1))
91 }
92 }
93 if err != nil {
94 return path.NewError(err)
95 }
96 return nil
97 case cty.Bool:
98 err := enc.EncodeBool(val.True())
99 if err != nil {
100 return path.NewError(err)
101 }
102 return nil
103 default:
104 panic("unsupported primitive type")
105 }
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
111 for it.Next() {
112 ek, ev := it.Element()
113 path[len(path)-1] = cty.IndexStep{
114 Key: ek,
115 }
116 err := marshal(ev, ety, path, enc)
117 if err != nil {
118 return err
119 }
120 }
121 return nil
122 case ty.IsMapType():
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
127 for it.Next() {
128 ek, ev := it.Element()
129 path[len(path)-1] = cty.IndexStep{
130 Key: ek,
131 }
132 var err error
133 err = marshal(ek, ek.Type(), path, enc)
134 if err != nil {
135 return err
136 }
137 err = marshal(ev, ety, path, enc)
138 if err != nil {
139 return err
140 }
141 }
142 return nil
143 case ty.IsTupleType():
144 etys := ty.TupleElementTypes()
145 it := val.ElementIterator()
146 path := append(path, nil) // local override of 'path' with extra element
147 i := 0
148 enc.EncodeArrayLen(len(etys))
149 for it.Next() {
150 ety := etys[i]
151 ek, ev := it.Element()
152 path[len(path)-1] = cty.IndexStep{
153 Key: ek,
154 }
155 err := marshal(ev, ety, path, enc)
156 if err != nil {
157 return err
158 }
159 i++
160 }
161 return nil
162 case ty.IsObjectType():
163 atys := ty.AttributeTypes()
164 path := append(path, nil) // local override of 'path' with extra element
165
166 names := make([]string, 0, len(atys))
167 for k := range atys {
168 names = append(names, k)
169 }
170 sort.Strings(names)
171
172 enc.EncodeMapLen(len(names))
173
174 for _, k := range names {
175 aty := atys[k]
176 av := val.GetAttr(k)
177 path[len(path)-1] = cty.GetAttrStep{
178 Name: k,
179 }
180 var err error
181 err = marshal(cty.StringVal(k), cty.String, path, enc)
182 if err != nil {
183 return err
184 }
185 err = marshal(av, aty, path, enc)
186 if err != nil {
187 return err
188 }
189 }
190 return nil
191 case ty.IsCapsuleType():
192 return path.NewErrorf("capsule types not supported for msgpack encoding")
193 default:
194 // should never happen
195 return path.NewErrorf("cannot msgpack-serialize %s", ty.FriendlyName())
196 }
197 }
198
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 {
202 dv := dynamicVal{
203 Value: val,
204 Path: path,
205 }
206 return enc.Encode(&dv)
207 }