8 "github.com/zclconf/go-cty/cty"
11 // ImpliedType returns the cty Type implied by the structure of the given
12 // JSON-compliant buffer. This function implements the default type mapping
13 // behavior used when decoding arbitrary JSON without explicit cty Type
16 // The rules are as follows:
18 // JSON strings, numbers and bools map to their equivalent primitive type in
21 // JSON objects map to cty object types, with the attributes defined by the
22 // object keys and the types of their values.
24 // JSON arrays map to cty tuple types, with the elements defined by the
25 // types of the array members.
27 // Any nulls are typed as DynamicPseudoType, so callers of this function
28 // must be prepared to deal with this. Callers that do not wish to deal with
29 // dynamic typing should not use this function and should instead describe
30 // their required types explicitly with a cty.Type instance when decoding.
32 // Any JSON syntax errors will be returned as an error, and the type will
33 // be the invalid value cty.NilType.
34 func ImpliedType(buf []byte) (cty.Type, error) {
35 r := bytes.NewReader(buf)
36 dec := json.NewDecoder(r)
39 ty, err := impliedType(dec)
41 return cty.NilType, err
45 return cty.NilType, fmt.Errorf("extraneous data after JSON object")
51 func impliedType(dec *json.Decoder) (cty.Type, error) {
52 tok, err := dec.Token()
54 return cty.NilType, err
57 return impliedTypeForTok(tok, dec)
60 func impliedTypeForTok(tok json.Token, dec *json.Decoder) (cty.Type, error) {
62 return cty.DynamicPseudoType, nil
65 switch ttok := tok.(type) {
70 return cty.Number, nil
73 return cty.String, nil
79 return impliedObjectType(dec)
81 return impliedTupleType(dec)
83 return cty.NilType, fmt.Errorf("unexpected token %q", ttok)
87 return cty.NilType, fmt.Errorf("unsupported JSON token %#v", tok)
91 func impliedObjectType(dec *json.Decoder) (cty.Type, error) {
92 // By the time we get in here, we've already consumed the { delimiter
93 // and so our next token should be the first object key.
95 var atys map[string]cty.Type
98 // Read the object key first
99 tok, err := dec.Token()
101 return cty.NilType, err
104 if ttok, ok := tok.(json.Delim); ok {
105 if rune(ttok) != '}' {
106 return cty.NilType, fmt.Errorf("unexpected delimiter %q", ttok)
111 key, ok := tok.(string)
113 return cty.NilType, fmt.Errorf("expected string but found %T", tok)
116 // Now read the value
117 tok, err = dec.Token()
119 return cty.NilType, err
122 aty, err := impliedTypeForTok(tok, dec)
124 return cty.NilType, err
128 atys = make(map[string]cty.Type)
134 return cty.EmptyObject, nil
137 return cty.Object(atys), nil
140 func impliedTupleType(dec *json.Decoder) (cty.Type, error) {
141 // By the time we get in here, we've already consumed the [ delimiter
142 // and so our next token should be the first value.
147 tok, err := dec.Token()
149 return cty.NilType, err
152 if ttok, ok := tok.(json.Delim); ok {
153 if rune(ttok) == ']' {
158 ety, err := impliedTypeForTok(tok, dec)
160 return cty.NilType, err
162 etys = append(etys, ety)
166 return cty.EmptyTuple, nil
169 return cty.Tuple(etys), nil