mirror of
				https://gitea.com/Lydanne/buildx.git
				synced 2025-11-04 01:53:42 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			460 lines
		
	
	
		
			9.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			460 lines
		
	
	
		
			9.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package json
 | 
						|
 | 
						|
import (
 | 
						|
	"bytes"
 | 
						|
	"encoding/json"
 | 
						|
	"fmt"
 | 
						|
	"reflect"
 | 
						|
 | 
						|
	"github.com/zclconf/go-cty/cty"
 | 
						|
	"github.com/zclconf/go-cty/cty/convert"
 | 
						|
)
 | 
						|
 | 
						|
func unmarshal(buf []byte, t cty.Type, path cty.Path) (cty.Value, error) {
 | 
						|
	dec := bufDecoder(buf)
 | 
						|
 | 
						|
	tok, err := dec.Token()
 | 
						|
	if err != nil {
 | 
						|
		return cty.NilVal, path.NewError(err)
 | 
						|
	}
 | 
						|
 | 
						|
	if tok == nil {
 | 
						|
		return cty.NullVal(t), nil
 | 
						|
	}
 | 
						|
 | 
						|
	if t == cty.DynamicPseudoType {
 | 
						|
		return unmarshalDynamic(buf, path)
 | 
						|
	}
 | 
						|
 | 
						|
	switch {
 | 
						|
	case t.IsPrimitiveType():
 | 
						|
		val, err := unmarshalPrimitive(tok, t, path)
 | 
						|
		if err != nil {
 | 
						|
			return cty.NilVal, err
 | 
						|
		}
 | 
						|
		return val, nil
 | 
						|
	case t.IsListType():
 | 
						|
		return unmarshalList(buf, t.ElementType(), path)
 | 
						|
	case t.IsSetType():
 | 
						|
		return unmarshalSet(buf, t.ElementType(), path)
 | 
						|
	case t.IsMapType():
 | 
						|
		return unmarshalMap(buf, t.ElementType(), path)
 | 
						|
	case t.IsTupleType():
 | 
						|
		return unmarshalTuple(buf, t.TupleElementTypes(), path)
 | 
						|
	case t.IsObjectType():
 | 
						|
		return unmarshalObject(buf, t.AttributeTypes(), path)
 | 
						|
	case t.IsCapsuleType():
 | 
						|
		return unmarshalCapsule(buf, t, path)
 | 
						|
	default:
 | 
						|
		return cty.NilVal, path.NewErrorf("unsupported type %s", t.FriendlyName())
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func unmarshalPrimitive(tok json.Token, t cty.Type, path cty.Path) (cty.Value, error) {
 | 
						|
 | 
						|
	switch t {
 | 
						|
	case cty.Bool:
 | 
						|
		switch v := tok.(type) {
 | 
						|
		case bool:
 | 
						|
			return cty.BoolVal(v), nil
 | 
						|
		case string:
 | 
						|
			val, err := convert.Convert(cty.StringVal(v), t)
 | 
						|
			if err != nil {
 | 
						|
				return cty.NilVal, path.NewError(err)
 | 
						|
			}
 | 
						|
			return val, nil
 | 
						|
		default:
 | 
						|
			return cty.NilVal, path.NewErrorf("bool is required")
 | 
						|
		}
 | 
						|
	case cty.Number:
 | 
						|
		if v, ok := tok.(json.Number); ok {
 | 
						|
			tok = string(v)
 | 
						|
		}
 | 
						|
		switch v := tok.(type) {
 | 
						|
		case string:
 | 
						|
			val, err := cty.ParseNumberVal(v)
 | 
						|
			if err != nil {
 | 
						|
				return cty.NilVal, path.NewError(err)
 | 
						|
			}
 | 
						|
			return val, nil
 | 
						|
		default:
 | 
						|
			return cty.NilVal, path.NewErrorf("number is required")
 | 
						|
		}
 | 
						|
	case cty.String:
 | 
						|
		switch v := tok.(type) {
 | 
						|
		case string:
 | 
						|
			return cty.StringVal(v), nil
 | 
						|
		case json.Number:
 | 
						|
			return cty.StringVal(string(v)), nil
 | 
						|
		case bool:
 | 
						|
			val, err := convert.Convert(cty.BoolVal(v), t)
 | 
						|
			if err != nil {
 | 
						|
				return cty.NilVal, path.NewError(err)
 | 
						|
			}
 | 
						|
			return val, nil
 | 
						|
		default:
 | 
						|
			return cty.NilVal, path.NewErrorf("string is required")
 | 
						|
		}
 | 
						|
	default:
 | 
						|
		// should never happen
 | 
						|
		panic("unsupported primitive type")
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func unmarshalList(buf []byte, ety cty.Type, path cty.Path) (cty.Value, error) {
 | 
						|
	dec := bufDecoder(buf)
 | 
						|
	if err := requireDelim(dec, '['); err != nil {
 | 
						|
		return cty.NilVal, path.NewError(err)
 | 
						|
	}
 | 
						|
 | 
						|
	var vals []cty.Value
 | 
						|
 | 
						|
	{
 | 
						|
		path := append(path, nil)
 | 
						|
		var idx int64
 | 
						|
 | 
						|
		for dec.More() {
 | 
						|
			path[len(path)-1] = cty.IndexStep{
 | 
						|
				Key: cty.NumberIntVal(idx),
 | 
						|
			}
 | 
						|
			idx++
 | 
						|
 | 
						|
			rawVal, err := readRawValue(dec)
 | 
						|
			if err != nil {
 | 
						|
				return cty.NilVal, path.NewErrorf("failed to read list value: %s", err)
 | 
						|
			}
 | 
						|
 | 
						|
			el, err := unmarshal(rawVal, ety, path)
 | 
						|
			if err != nil {
 | 
						|
				return cty.NilVal, err
 | 
						|
			}
 | 
						|
 | 
						|
			vals = append(vals, el)
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if err := requireDelim(dec, ']'); err != nil {
 | 
						|
		return cty.NilVal, path.NewError(err)
 | 
						|
	}
 | 
						|
 | 
						|
	if len(vals) == 0 {
 | 
						|
		return cty.ListValEmpty(ety), nil
 | 
						|
	}
 | 
						|
 | 
						|
	return cty.ListVal(vals), nil
 | 
						|
}
 | 
						|
 | 
						|
func unmarshalSet(buf []byte, ety cty.Type, path cty.Path) (cty.Value, error) {
 | 
						|
	dec := bufDecoder(buf)
 | 
						|
	if err := requireDelim(dec, '['); err != nil {
 | 
						|
		return cty.NilVal, path.NewError(err)
 | 
						|
	}
 | 
						|
 | 
						|
	var vals []cty.Value
 | 
						|
 | 
						|
	{
 | 
						|
		path := append(path, nil)
 | 
						|
 | 
						|
		for dec.More() {
 | 
						|
			path[len(path)-1] = cty.IndexStep{
 | 
						|
				Key: cty.UnknownVal(ety),
 | 
						|
			}
 | 
						|
 | 
						|
			rawVal, err := readRawValue(dec)
 | 
						|
			if err != nil {
 | 
						|
				return cty.NilVal, path.NewErrorf("failed to read set value: %s", err)
 | 
						|
			}
 | 
						|
 | 
						|
			el, err := unmarshal(rawVal, ety, path)
 | 
						|
			if err != nil {
 | 
						|
				return cty.NilVal, err
 | 
						|
			}
 | 
						|
 | 
						|
			vals = append(vals, el)
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if err := requireDelim(dec, ']'); err != nil {
 | 
						|
		return cty.NilVal, path.NewError(err)
 | 
						|
	}
 | 
						|
 | 
						|
	if len(vals) == 0 {
 | 
						|
		return cty.SetValEmpty(ety), nil
 | 
						|
	}
 | 
						|
 | 
						|
	return cty.SetVal(vals), nil
 | 
						|
}
 | 
						|
 | 
						|
func unmarshalMap(buf []byte, ety cty.Type, path cty.Path) (cty.Value, error) {
 | 
						|
	dec := bufDecoder(buf)
 | 
						|
	if err := requireDelim(dec, '{'); err != nil {
 | 
						|
		return cty.NilVal, path.NewError(err)
 | 
						|
	}
 | 
						|
 | 
						|
	vals := make(map[string]cty.Value)
 | 
						|
 | 
						|
	{
 | 
						|
		path := append(path, nil)
 | 
						|
 | 
						|
		for dec.More() {
 | 
						|
			path[len(path)-1] = cty.IndexStep{
 | 
						|
				Key: cty.UnknownVal(cty.String),
 | 
						|
			}
 | 
						|
 | 
						|
			var err error
 | 
						|
 | 
						|
			k, err := requireObjectKey(dec)
 | 
						|
			if err != nil {
 | 
						|
				return cty.NilVal, path.NewErrorf("failed to read map key: %s", err)
 | 
						|
			}
 | 
						|
 | 
						|
			path[len(path)-1] = cty.IndexStep{
 | 
						|
				Key: cty.StringVal(k),
 | 
						|
			}
 | 
						|
 | 
						|
			rawVal, err := readRawValue(dec)
 | 
						|
			if err != nil {
 | 
						|
				return cty.NilVal, path.NewErrorf("failed to read map value: %s", err)
 | 
						|
			}
 | 
						|
 | 
						|
			el, err := unmarshal(rawVal, ety, path)
 | 
						|
			if err != nil {
 | 
						|
				return cty.NilVal, err
 | 
						|
			}
 | 
						|
 | 
						|
			vals[k] = el
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if err := requireDelim(dec, '}'); err != nil {
 | 
						|
		return cty.NilVal, path.NewError(err)
 | 
						|
	}
 | 
						|
 | 
						|
	if len(vals) == 0 {
 | 
						|
		return cty.MapValEmpty(ety), nil
 | 
						|
	}
 | 
						|
 | 
						|
	return cty.MapVal(vals), nil
 | 
						|
}
 | 
						|
 | 
						|
func unmarshalTuple(buf []byte, etys []cty.Type, path cty.Path) (cty.Value, error) {
 | 
						|
	dec := bufDecoder(buf)
 | 
						|
	if err := requireDelim(dec, '['); err != nil {
 | 
						|
		return cty.NilVal, path.NewError(err)
 | 
						|
	}
 | 
						|
 | 
						|
	var vals []cty.Value
 | 
						|
 | 
						|
	{
 | 
						|
		path := append(path, nil)
 | 
						|
		var idx int
 | 
						|
 | 
						|
		for dec.More() {
 | 
						|
			if idx >= len(etys) {
 | 
						|
				return cty.NilVal, path[:len(path)-1].NewErrorf("too many tuple elements (need %d)", len(etys))
 | 
						|
			}
 | 
						|
 | 
						|
			path[len(path)-1] = cty.IndexStep{
 | 
						|
				Key: cty.NumberIntVal(int64(idx)),
 | 
						|
			}
 | 
						|
			ety := etys[idx]
 | 
						|
			idx++
 | 
						|
 | 
						|
			rawVal, err := readRawValue(dec)
 | 
						|
			if err != nil {
 | 
						|
				return cty.NilVal, path.NewErrorf("failed to read tuple value: %s", err)
 | 
						|
			}
 | 
						|
 | 
						|
			el, err := unmarshal(rawVal, ety, path)
 | 
						|
			if err != nil {
 | 
						|
				return cty.NilVal, err
 | 
						|
			}
 | 
						|
 | 
						|
			vals = append(vals, el)
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if err := requireDelim(dec, ']'); err != nil {
 | 
						|
		return cty.NilVal, path.NewError(err)
 | 
						|
	}
 | 
						|
 | 
						|
	if len(vals) != len(etys) {
 | 
						|
		return cty.NilVal, path[:len(path)-1].NewErrorf("not enough tuple elements (need %d)", len(etys))
 | 
						|
	}
 | 
						|
 | 
						|
	if len(vals) == 0 {
 | 
						|
		return cty.EmptyTupleVal, nil
 | 
						|
	}
 | 
						|
 | 
						|
	return cty.TupleVal(vals), nil
 | 
						|
}
 | 
						|
 | 
						|
func unmarshalObject(buf []byte, atys map[string]cty.Type, path cty.Path) (cty.Value, error) {
 | 
						|
	dec := bufDecoder(buf)
 | 
						|
	if err := requireDelim(dec, '{'); err != nil {
 | 
						|
		return cty.NilVal, path.NewError(err)
 | 
						|
	}
 | 
						|
 | 
						|
	vals := make(map[string]cty.Value)
 | 
						|
 | 
						|
	{
 | 
						|
		objPath := path           // some errors report from the object's perspective
 | 
						|
		path := append(path, nil) // path to a specific attribute
 | 
						|
 | 
						|
		for dec.More() {
 | 
						|
 | 
						|
			var err error
 | 
						|
 | 
						|
			k, err := requireObjectKey(dec)
 | 
						|
			if err != nil {
 | 
						|
				return cty.NilVal, path.NewErrorf("failed to read object key: %s", err)
 | 
						|
			}
 | 
						|
 | 
						|
			aty, ok := atys[k]
 | 
						|
			if !ok {
 | 
						|
				return cty.NilVal, objPath.NewErrorf("unsupported attribute %q", k)
 | 
						|
			}
 | 
						|
 | 
						|
			path[len(path)-1] = cty.GetAttrStep{
 | 
						|
				Name: k,
 | 
						|
			}
 | 
						|
 | 
						|
			rawVal, err := readRawValue(dec)
 | 
						|
			if err != nil {
 | 
						|
				return cty.NilVal, path.NewErrorf("failed to read object value: %s", err)
 | 
						|
			}
 | 
						|
 | 
						|
			el, err := unmarshal(rawVal, aty, path)
 | 
						|
			if err != nil {
 | 
						|
				return cty.NilVal, err
 | 
						|
			}
 | 
						|
 | 
						|
			vals[k] = el
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if err := requireDelim(dec, '}'); err != nil {
 | 
						|
		return cty.NilVal, path.NewError(err)
 | 
						|
	}
 | 
						|
 | 
						|
	// Make sure we have a value for every attribute
 | 
						|
	for k, aty := range atys {
 | 
						|
		if _, exists := vals[k]; !exists {
 | 
						|
			vals[k] = cty.NullVal(aty)
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if len(vals) == 0 {
 | 
						|
		return cty.EmptyObjectVal, nil
 | 
						|
	}
 | 
						|
 | 
						|
	return cty.ObjectVal(vals), nil
 | 
						|
}
 | 
						|
 | 
						|
func unmarshalCapsule(buf []byte, t cty.Type, path cty.Path) (cty.Value, error) {
 | 
						|
	rawType := t.EncapsulatedType()
 | 
						|
	ptrPtr := reflect.New(reflect.PtrTo(rawType))
 | 
						|
	ptrPtr.Elem().Set(reflect.New(rawType))
 | 
						|
	ptr := ptrPtr.Elem().Interface()
 | 
						|
	err := json.Unmarshal(buf, ptr)
 | 
						|
	if err != nil {
 | 
						|
		return cty.NilVal, path.NewError(err)
 | 
						|
	}
 | 
						|
 | 
						|
	return cty.CapsuleVal(t, ptr), nil
 | 
						|
}
 | 
						|
 | 
						|
func unmarshalDynamic(buf []byte, path cty.Path) (cty.Value, error) {
 | 
						|
	dec := bufDecoder(buf)
 | 
						|
	if err := requireDelim(dec, '{'); err != nil {
 | 
						|
		return cty.NilVal, path.NewError(err)
 | 
						|
	}
 | 
						|
 | 
						|
	var t cty.Type
 | 
						|
	var valBody []byte // defer actual decoding until we know the type
 | 
						|
 | 
						|
	for dec.More() {
 | 
						|
		var err error
 | 
						|
 | 
						|
		key, err := requireObjectKey(dec)
 | 
						|
		if err != nil {
 | 
						|
			return cty.NilVal, path.NewErrorf("failed to read dynamic type descriptor key: %s", err)
 | 
						|
		}
 | 
						|
 | 
						|
		rawVal, err := readRawValue(dec)
 | 
						|
		if err != nil {
 | 
						|
			return cty.NilVal, path.NewErrorf("failed to read dynamic type descriptor value: %s", err)
 | 
						|
		}
 | 
						|
 | 
						|
		switch key {
 | 
						|
		case "type":
 | 
						|
			err := json.Unmarshal(rawVal, &t)
 | 
						|
			if err != nil {
 | 
						|
				return cty.NilVal, path.NewErrorf("failed to decode type for dynamic value: %s", err)
 | 
						|
			}
 | 
						|
		case "value":
 | 
						|
			valBody = rawVal
 | 
						|
		default:
 | 
						|
			return cty.NilVal, path.NewErrorf("invalid key %q in dynamically-typed value", key)
 | 
						|
		}
 | 
						|
 | 
						|
	}
 | 
						|
 | 
						|
	if err := requireDelim(dec, '}'); err != nil {
 | 
						|
		return cty.NilVal, path.NewError(err)
 | 
						|
	}
 | 
						|
 | 
						|
	if t == cty.NilType {
 | 
						|
		return cty.NilVal, path.NewErrorf("missing type in dynamically-typed value")
 | 
						|
	}
 | 
						|
	if valBody == nil {
 | 
						|
		return cty.NilVal, path.NewErrorf("missing value in dynamically-typed value")
 | 
						|
	}
 | 
						|
 | 
						|
	val, err := Unmarshal([]byte(valBody), t)
 | 
						|
	if err != nil {
 | 
						|
		return cty.NilVal, path.NewError(err)
 | 
						|
	}
 | 
						|
	return val, nil
 | 
						|
}
 | 
						|
 | 
						|
func requireDelim(dec *json.Decoder, d rune) error {
 | 
						|
	tok, err := dec.Token()
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
 | 
						|
	if tok != json.Delim(d) {
 | 
						|
		return fmt.Errorf("missing expected %c", d)
 | 
						|
	}
 | 
						|
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func requireObjectKey(dec *json.Decoder) (string, error) {
 | 
						|
	tok, err := dec.Token()
 | 
						|
	if err != nil {
 | 
						|
		return "", err
 | 
						|
	}
 | 
						|
	if s, ok := tok.(string); ok {
 | 
						|
		return s, nil
 | 
						|
	}
 | 
						|
	return "", fmt.Errorf("missing expected object key")
 | 
						|
}
 | 
						|
 | 
						|
func readRawValue(dec *json.Decoder) ([]byte, error) {
 | 
						|
	var rawVal json.RawMessage
 | 
						|
	err := dec.Decode(&rawVal)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	return []byte(rawVal), nil
 | 
						|
}
 | 
						|
 | 
						|
func bufDecoder(buf []byte) *json.Decoder {
 | 
						|
	r := bytes.NewReader(buf)
 | 
						|
	dec := json.NewDecoder(r)
 | 
						|
	dec.UseNumber()
 | 
						|
	return dec
 | 
						|
}
 |