8 "github.com/vmihailenco/msgpack"
9 msgpackcodes "github.com/vmihailenco/msgpack/codes"
10 "github.com/zclconf/go-cty/cty"
13 // ImpliedType returns the cty Type implied by the structure of the given
14 // msgpack-compliant buffer. This function implements the default type mapping
15 // behavior used when decoding arbitrary msgpack without explicit cty Type
18 // The rules are as follows:
20 // msgpack strings, numbers and bools map to their equivalent primitive type in
23 // msgpack maps become cty object types, with the attributes defined by the
24 // map keys and the types of their values.
26 // msgpack arrays become cty tuple types, with the elements defined by the
27 // types of the array members.
29 // Any nulls are typed as DynamicPseudoType, so callers of this function
30 // must be prepared to deal with this. Callers that do not wish to deal with
31 // dynamic typing should not use this function and should instead describe
32 // their required types explicitly with a cty.Type instance when decoding.
34 // Any unknown values are similarly typed as DynamicPseudoType, because these
35 // do not carry type information on the wire.
37 // Any parse errors will be returned as an error, and the type will be the
38 // invalid value cty.NilType.
39 func ImpliedType(buf []byte) (cty.Type, error) {
40 r := bytes.NewReader(buf)
41 dec := msgpack.NewDecoder(r)
43 ty, err := impliedType(dec)
45 return cty.NilType, err
48 // We must now be at the end of the buffer
51 return ty, fmt.Errorf("extra bytes after msgpack value")
57 func impliedType(dec *msgpack.Decoder) (cty.Type, error) {
58 // If this function returns with a nil error then it must have already
59 // consumed the next value from the decoder, since when called recursively
60 // the caller will be expecting to find a following value here.
62 code, err := dec.PeekCode()
64 return cty.NilType, err
69 case code == msgpackcodes.Nil || msgpackcodes.IsExt(code):
71 return cty.DynamicPseudoType, err
73 case code == msgpackcodes.True || code == msgpackcodes.False:
74 _, err := dec.DecodeBool()
77 case msgpackcodes.IsFixedNum(code):
78 _, err := dec.DecodeInt64()
79 return cty.Number, err
81 case code == msgpackcodes.Int8 || code == msgpackcodes.Int16 || code == msgpackcodes.Int32 || code == msgpackcodes.Int64:
82 _, err := dec.DecodeInt64()
83 return cty.Number, err
85 case code == msgpackcodes.Uint8 || code == msgpackcodes.Uint16 || code == msgpackcodes.Uint32 || code == msgpackcodes.Uint64:
86 _, err := dec.DecodeUint64()
87 return cty.Number, err
89 case code == msgpackcodes.Float || code == msgpackcodes.Double:
90 _, err := dec.DecodeFloat64()
91 return cty.Number, err
93 case msgpackcodes.IsString(code):
94 _, err := dec.DecodeString()
95 return cty.String, err
97 case msgpackcodes.IsFixedMap(code) || code == msgpackcodes.Map16 || code == msgpackcodes.Map32:
98 return impliedObjectType(dec)
100 case msgpackcodes.IsFixedArray(code) || code == msgpackcodes.Array16 || code == msgpackcodes.Array32:
101 return impliedTupleType(dec)
104 return cty.NilType, fmt.Errorf("unsupported msgpack code %#v", code)
108 func impliedObjectType(dec *msgpack.Decoder) (cty.Type, error) {
109 // If we get in here then we've already peeked the next code and know
110 // it's some sort of map.
111 l, err := dec.DecodeMapLen()
113 return cty.DynamicPseudoType, nil
116 var atys map[string]cty.Type
118 for i := 0; i < l; i++ {
119 // Read the map key first. We require maps to be strings, but msgpack
120 // doesn't so we're prepared to error here if not.
121 k, err := dec.DecodeString()
123 return cty.DynamicPseudoType, err
126 aty, err := impliedType(dec)
128 return cty.DynamicPseudoType, err
132 atys = make(map[string]cty.Type)
138 return cty.EmptyObject, nil
141 return cty.Object(atys), nil
144 func impliedTupleType(dec *msgpack.Decoder) (cty.Type, error) {
145 // If we get in here then we've already peeked the next code and know
146 // it's some sort of array.
147 l, err := dec.DecodeArrayLen()
149 return cty.DynamicPseudoType, nil
153 return cty.EmptyTuple, nil
156 etys := make([]cty.Type, l)
158 for i := 0; i < l; i++ {
159 ety, err := impliedType(dec)
161 return cty.DynamicPseudoType, err
166 return cty.Tuple(etys), nil