1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170
|
package json
import (
"bytes"
"encoding/json"
"fmt"
"github.com/zclconf/go-cty/cty"
)
// ImpliedType returns the cty Type implied by the structure of the given
// JSON-compliant buffer. This function implements the default type mapping
// behavior used when decoding arbitrary JSON without explicit cty Type
// information.
//
// The rules are as follows:
//
// JSON strings, numbers and bools map to their equivalent primitive type in
// cty.
//
// JSON objects map to cty object types, with the attributes defined by the
// object keys and the types of their values.
//
// JSON arrays map to cty tuple types, with the elements defined by the
// types of the array members.
//
// Any nulls are typed as DynamicPseudoType, so callers of this function
// must be prepared to deal with this. Callers that do not wish to deal with
// dynamic typing should not use this function and should instead describe
// their required types explicitly with a cty.Type instance when decoding.
//
// Any JSON syntax errors will be returned as an error, and the type will
// be the invalid value cty.NilType.
func ImpliedType(buf []byte) (cty.Type, error) {
r := bytes.NewReader(buf)
dec := json.NewDecoder(r)
dec.UseNumber()
ty, err := impliedType(dec)
if err != nil {
return cty.NilType, err
}
if dec.More() {
return cty.NilType, fmt.Errorf("extraneous data after JSON object")
}
return ty, nil
}
func impliedType(dec *json.Decoder) (cty.Type, error) {
tok, err := dec.Token()
if err != nil {
return cty.NilType, err
}
return impliedTypeForTok(tok, dec)
}
func impliedTypeForTok(tok json.Token, dec *json.Decoder) (cty.Type, error) {
if tok == nil {
return cty.DynamicPseudoType, nil
}
switch ttok := tok.(type) {
case bool:
return cty.Bool, nil
case json.Number:
return cty.Number, nil
case string:
return cty.String, nil
case json.Delim:
switch rune(ttok) {
case '{':
return impliedObjectType(dec)
case '[':
return impliedTupleType(dec)
default:
return cty.NilType, fmt.Errorf("unexpected token %q", ttok)
}
default:
return cty.NilType, fmt.Errorf("unsupported JSON token %#v", tok)
}
}
func impliedObjectType(dec *json.Decoder) (cty.Type, error) {
// By the time we get in here, we've already consumed the { delimiter
// and so our next token should be the first object key.
var atys map[string]cty.Type
for {
// Read the object key first
tok, err := dec.Token()
if err != nil {
return cty.NilType, err
}
if ttok, ok := tok.(json.Delim); ok {
if rune(ttok) != '}' {
return cty.NilType, fmt.Errorf("unexpected delimiter %q", ttok)
}
break
}
key, ok := tok.(string)
if !ok {
return cty.NilType, fmt.Errorf("expected string but found %T", tok)
}
// Now read the value
tok, err = dec.Token()
if err != nil {
return cty.NilType, err
}
aty, err := impliedTypeForTok(tok, dec)
if err != nil {
return cty.NilType, err
}
if atys == nil {
atys = make(map[string]cty.Type)
}
atys[key] = aty
}
if len(atys) == 0 {
return cty.EmptyObject, nil
}
return cty.Object(atys), nil
}
func impliedTupleType(dec *json.Decoder) (cty.Type, error) {
// By the time we get in here, we've already consumed the [ delimiter
// and so our next token should be the first value.
var etys []cty.Type
for {
tok, err := dec.Token()
if err != nil {
return cty.NilType, err
}
if ttok, ok := tok.(json.Delim); ok {
if rune(ttok) == ']' {
break
}
}
ety, err := impliedTypeForTok(tok, dec)
if err != nil {
return cty.NilType, err
}
etys = append(etys, ety)
}
if len(etys) == 0 {
return cty.EmptyTuple, nil
}
return cty.Tuple(etys), nil
}
|