terraform-provider-google/vendor/github.com/zclconf/go-cty/cty/json/type_implied.go
Paddy 961c878e0d Switch to using Go modules. (#2679)
Switch to using Go modules.

This migrates our vendor.json to use Go 1.11's modules system, and
replaces the vendor folder with the output of go mod vendor.

The vendored code should remain basically the same; I believe some
tree shaking of packages and support scripts/licenses/READMEs/etc.
happened.

This also fixes Travis and our Makefile to no longer use govendor.
2018-12-20 17:22:22 -08:00

171 lines
3.6 KiB
Go

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
}