You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
terraformDummyRepo2/vendor/github.com/hashicorp/go-cty/cty/json/type_implied.go

171 lines
3.6 KiB
Go

package json
import (
"bytes"
"encoding/json"
"fmt"
"github.com/hashicorp/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
}