6 "github.com/vmihailenco/msgpack"
7 msgpackCodes "github.com/vmihailenco/msgpack/codes"
8 "github.com/zclconf/go-cty/cty"
11 // Unmarshal interprets the given bytes as a msgpack-encoded cty Value of
12 // the given type, returning the result.
14 // If an error is returned, the error is written with a hypothetical
15 // end-user that wrote the msgpack file as its audience, using cty type
16 // system concepts rather than Go type system concepts.
17 func Unmarshal(b []byte, ty cty.Type) (cty.Value, error) {
18 r := bytes.NewReader(b)
19 dec := msgpack.NewDecoder(r)
22 return unmarshal(dec, ty, path)
25 func unmarshal(dec *msgpack.Decoder, ty cty.Type, path cty.Path) (cty.Value, error) {
26 peek, err := dec.PeekCode()
28 return cty.DynamicVal, path.NewError(err)
30 if msgpackCodes.IsExt(peek) {
31 // We just assume _all_ extensions are unknown values,
32 // since we don't have any other extensions.
33 dec.Skip() // skip what we've peeked
34 return cty.UnknownVal(ty), nil
36 if ty == cty.DynamicPseudoType {
37 return unmarshalDynamic(dec, path)
39 if peek == msgpackCodes.Nil {
40 dec.Skip() // skip what we've peeked
41 return cty.NullVal(ty), nil
45 case ty.IsPrimitiveType():
46 val, err := unmarshalPrimitive(dec, ty, path)
48 return cty.NilVal, err
52 return unmarshalList(dec, ty.ElementType(), path)
54 return unmarshalSet(dec, ty.ElementType(), path)
56 return unmarshalMap(dec, ty.ElementType(), path)
57 case ty.IsTupleType():
58 return unmarshalTuple(dec, ty.TupleElementTypes(), path)
59 case ty.IsObjectType():
60 return unmarshalObject(dec, ty.AttributeTypes(), path)
62 return cty.NilVal, path.NewErrorf("unsupported type %s", ty.FriendlyName())
66 func unmarshalPrimitive(dec *msgpack.Decoder, ty cty.Type, path cty.Path) (cty.Value, error) {
69 rv, err := dec.DecodeBool()
71 return cty.DynamicVal, path.NewErrorf("bool is required")
73 return cty.BoolVal(rv), nil
75 // Marshal will try int and float first, if the value can be
76 // losslessly represented in these encodings, and then fall
77 // back on a string if the number is too large or too precise.
78 peek, err := dec.PeekCode()
80 return cty.DynamicVal, path.NewErrorf("number is required")
83 if msgpackCodes.IsFixedNum(peek) {
84 rv, err := dec.DecodeInt64()
86 return cty.DynamicVal, path.NewErrorf("number is required")
88 return cty.NumberIntVal(rv), nil
92 case msgpackCodes.Int8, msgpackCodes.Int16, msgpackCodes.Int32, msgpackCodes.Int64:
93 rv, err := dec.DecodeInt64()
95 return cty.DynamicVal, path.NewErrorf("number is required")
97 return cty.NumberIntVal(rv), nil
98 case msgpackCodes.Uint8, msgpackCodes.Uint16, msgpackCodes.Uint32, msgpackCodes.Uint64:
99 rv, err := dec.DecodeUint64()
101 return cty.DynamicVal, path.NewErrorf("number is required")
103 return cty.NumberUIntVal(rv), nil
104 case msgpackCodes.Float, msgpackCodes.Double:
105 rv, err := dec.DecodeFloat64()
107 return cty.DynamicVal, path.NewErrorf("number is required")
109 return cty.NumberFloatVal(rv), nil
111 rv, err := dec.DecodeString()
113 return cty.DynamicVal, path.NewErrorf("number is required")
115 v, err := cty.ParseNumberVal(rv)
117 return cty.DynamicVal, path.NewErrorf("number is required")
122 rv, err := dec.DecodeString()
124 return cty.DynamicVal, path.NewErrorf("string is required")
126 return cty.StringVal(rv), nil
128 // should never happen
129 panic("unsupported primitive type")
133 func unmarshalList(dec *msgpack.Decoder, ety cty.Type, path cty.Path) (cty.Value, error) {
134 length, err := dec.DecodeArrayLen()
136 return cty.DynamicVal, path.NewErrorf("a list is required")
141 return cty.NullVal(cty.List(ety)), nil
143 return cty.ListValEmpty(ety), nil
146 vals := make([]cty.Value, 0, length)
147 path = append(path, nil)
148 for i := 0; i < length; i++ {
149 path[len(path)-1] = cty.IndexStep{
150 Key: cty.NumberIntVal(int64(i)),
153 val, err := unmarshal(dec, ety, path)
155 return cty.DynamicVal, err
158 vals = append(vals, val)
161 return cty.ListVal(vals), nil
164 func unmarshalSet(dec *msgpack.Decoder, ety cty.Type, path cty.Path) (cty.Value, error) {
165 length, err := dec.DecodeArrayLen()
167 return cty.DynamicVal, path.NewErrorf("a set is required")
172 return cty.NullVal(cty.Set(ety)), nil
174 return cty.SetValEmpty(ety), nil
177 vals := make([]cty.Value, 0, length)
178 path = append(path, nil)
179 for i := 0; i < length; i++ {
180 path[len(path)-1] = cty.IndexStep{
181 Key: cty.NumberIntVal(int64(i)),
184 val, err := unmarshal(dec, ety, path)
186 return cty.DynamicVal, err
189 vals = append(vals, val)
192 return cty.SetVal(vals), nil
195 func unmarshalMap(dec *msgpack.Decoder, ety cty.Type, path cty.Path) (cty.Value, error) {
196 length, err := dec.DecodeMapLen()
198 return cty.DynamicVal, path.NewErrorf("a map is required")
203 return cty.NullVal(cty.Map(ety)), nil
205 return cty.MapValEmpty(ety), nil
208 vals := make(map[string]cty.Value, length)
209 path = append(path, nil)
210 for i := 0; i < length; i++ {
211 key, err := dec.DecodeString()
213 path[:len(path)-1].NewErrorf("non-string key in map")
216 path[len(path)-1] = cty.IndexStep{
217 Key: cty.StringVal(key),
220 val, err := unmarshal(dec, ety, path)
222 return cty.DynamicVal, err
228 return cty.MapVal(vals), nil
231 func unmarshalTuple(dec *msgpack.Decoder, etys []cty.Type, path cty.Path) (cty.Value, error) {
232 length, err := dec.DecodeArrayLen()
234 return cty.DynamicVal, path.NewErrorf("a tuple is required")
239 return cty.NullVal(cty.Tuple(etys)), nil
241 return cty.TupleVal(nil), nil
242 case length != len(etys):
243 return cty.DynamicVal, path.NewErrorf("a tuple of length %d is required", len(etys))
246 vals := make([]cty.Value, 0, length)
247 path = append(path, nil)
248 for i := 0; i < length; i++ {
249 path[len(path)-1] = cty.IndexStep{
250 Key: cty.NumberIntVal(int64(i)),
254 val, err := unmarshal(dec, ety, path)
256 return cty.DynamicVal, err
259 vals = append(vals, val)
262 return cty.TupleVal(vals), nil
265 func unmarshalObject(dec *msgpack.Decoder, atys map[string]cty.Type, path cty.Path) (cty.Value, error) {
266 length, err := dec.DecodeMapLen()
268 return cty.DynamicVal, path.NewErrorf("an object is required")
273 return cty.NullVal(cty.Object(atys)), nil
275 return cty.ObjectVal(nil), nil
276 case length != len(atys):
277 return cty.DynamicVal, path.NewErrorf("an object with %d attributes is required (%d given)",
281 vals := make(map[string]cty.Value, length)
282 path = append(path, nil)
283 for i := 0; i < length; i++ {
284 key, err := dec.DecodeString()
286 return cty.DynamicVal, path[:len(path)-1].NewErrorf("all keys must be strings")
289 path[len(path)-1] = cty.IndexStep{
290 Key: cty.StringVal(key),
292 aty, exists := atys[key]
294 return cty.DynamicVal, path.NewErrorf("unsupported attribute")
297 val, err := unmarshal(dec, aty, path)
299 return cty.DynamicVal, err
305 return cty.ObjectVal(vals), nil
308 func unmarshalDynamic(dec *msgpack.Decoder, path cty.Path) (cty.Value, error) {
309 length, err := dec.DecodeArrayLen()
311 return cty.DynamicVal, path.NewError(err)
316 return cty.NullVal(cty.DynamicPseudoType), nil
318 return cty.DynamicVal, path.NewErrorf(
319 "dynamic value array must have exactly two elements",
323 typeJSON, err := dec.DecodeBytes()
325 return cty.DynamicVal, path.NewError(err)
328 err = (&ty).UnmarshalJSON(typeJSON)
330 return cty.DynamicVal, path.NewError(err)
333 return unmarshal(dec, ty, path)