mirror of
				https://gitea.com/Lydanne/buildx.git
				synced 2025-11-04 18:13:42 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			194 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			194 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package json
 | 
						|
 | 
						|
import (
 | 
						|
	"bytes"
 | 
						|
	"encoding/json"
 | 
						|
	"sort"
 | 
						|
 | 
						|
	"github.com/zclconf/go-cty/cty"
 | 
						|
)
 | 
						|
 | 
						|
func marshal(val cty.Value, t cty.Type, path cty.Path, b *bytes.Buffer) error {
 | 
						|
	if val.IsMarked() {
 | 
						|
		return path.NewErrorf("value has marks, so it cannot be serialized as JSON")
 | 
						|
	}
 | 
						|
 | 
						|
	// If we're going to decode as DynamicPseudoType then we need to save
 | 
						|
	// dynamic type information to recover the real type.
 | 
						|
	if t == cty.DynamicPseudoType && val.Type() != cty.DynamicPseudoType {
 | 
						|
		return marshalDynamic(val, path, b)
 | 
						|
	}
 | 
						|
 | 
						|
	if val.IsNull() {
 | 
						|
		b.WriteString("null")
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
 | 
						|
	if !val.IsKnown() {
 | 
						|
		return path.NewErrorf("value is not known")
 | 
						|
	}
 | 
						|
 | 
						|
	// The caller should've guaranteed that the given val is conformant with
 | 
						|
	// the given type t, so we'll proceed under that assumption here.
 | 
						|
 | 
						|
	switch {
 | 
						|
	case t.IsPrimitiveType():
 | 
						|
		switch t {
 | 
						|
		case cty.String:
 | 
						|
			json, err := json.Marshal(val.AsString())
 | 
						|
			if err != nil {
 | 
						|
				return path.NewErrorf("failed to serialize value: %s", err)
 | 
						|
			}
 | 
						|
			b.Write(json)
 | 
						|
			return nil
 | 
						|
		case cty.Number:
 | 
						|
			if val.RawEquals(cty.PositiveInfinity) || val.RawEquals(cty.NegativeInfinity) {
 | 
						|
				return path.NewErrorf("cannot serialize infinity as JSON")
 | 
						|
			}
 | 
						|
			b.WriteString(val.AsBigFloat().Text('f', -1))
 | 
						|
			return nil
 | 
						|
		case cty.Bool:
 | 
						|
			if val.True() {
 | 
						|
				b.WriteString("true")
 | 
						|
			} else {
 | 
						|
				b.WriteString("false")
 | 
						|
			}
 | 
						|
			return nil
 | 
						|
		default:
 | 
						|
			panic("unsupported primitive type")
 | 
						|
		}
 | 
						|
	case t.IsListType(), t.IsSetType():
 | 
						|
		b.WriteRune('[')
 | 
						|
		first := true
 | 
						|
		ety := t.ElementType()
 | 
						|
		it := val.ElementIterator()
 | 
						|
		path := append(path, nil) // local override of 'path' with extra element
 | 
						|
		for it.Next() {
 | 
						|
			if !first {
 | 
						|
				b.WriteRune(',')
 | 
						|
			}
 | 
						|
			ek, ev := it.Element()
 | 
						|
			path[len(path)-1] = cty.IndexStep{
 | 
						|
				Key: ek,
 | 
						|
			}
 | 
						|
			err := marshal(ev, ety, path, b)
 | 
						|
			if err != nil {
 | 
						|
				return err
 | 
						|
			}
 | 
						|
			first = false
 | 
						|
		}
 | 
						|
		b.WriteRune(']')
 | 
						|
		return nil
 | 
						|
	case t.IsMapType():
 | 
						|
		b.WriteRune('{')
 | 
						|
		first := true
 | 
						|
		ety := t.ElementType()
 | 
						|
		it := val.ElementIterator()
 | 
						|
		path := append(path, nil) // local override of 'path' with extra element
 | 
						|
		for it.Next() {
 | 
						|
			if !first {
 | 
						|
				b.WriteRune(',')
 | 
						|
			}
 | 
						|
			ek, ev := it.Element()
 | 
						|
			path[len(path)-1] = cty.IndexStep{
 | 
						|
				Key: ek,
 | 
						|
			}
 | 
						|
			var err error
 | 
						|
			err = marshal(ek, ek.Type(), path, b)
 | 
						|
			if err != nil {
 | 
						|
				return err
 | 
						|
			}
 | 
						|
			b.WriteRune(':')
 | 
						|
			err = marshal(ev, ety, path, b)
 | 
						|
			if err != nil {
 | 
						|
				return err
 | 
						|
			}
 | 
						|
			first = false
 | 
						|
		}
 | 
						|
		b.WriteRune('}')
 | 
						|
		return nil
 | 
						|
	case t.IsTupleType():
 | 
						|
		b.WriteRune('[')
 | 
						|
		etys := t.TupleElementTypes()
 | 
						|
		it := val.ElementIterator()
 | 
						|
		path := append(path, nil) // local override of 'path' with extra element
 | 
						|
		i := 0
 | 
						|
		for it.Next() {
 | 
						|
			if i > 0 {
 | 
						|
				b.WriteRune(',')
 | 
						|
			}
 | 
						|
			ety := etys[i]
 | 
						|
			ek, ev := it.Element()
 | 
						|
			path[len(path)-1] = cty.IndexStep{
 | 
						|
				Key: ek,
 | 
						|
			}
 | 
						|
			err := marshal(ev, ety, path, b)
 | 
						|
			if err != nil {
 | 
						|
				return err
 | 
						|
			}
 | 
						|
			i++
 | 
						|
		}
 | 
						|
		b.WriteRune(']')
 | 
						|
		return nil
 | 
						|
	case t.IsObjectType():
 | 
						|
		b.WriteRune('{')
 | 
						|
		atys := t.AttributeTypes()
 | 
						|
		path := append(path, nil) // local override of 'path' with extra element
 | 
						|
 | 
						|
		names := make([]string, 0, len(atys))
 | 
						|
		for k := range atys {
 | 
						|
			names = append(names, k)
 | 
						|
		}
 | 
						|
		sort.Strings(names)
 | 
						|
 | 
						|
		for i, k := range names {
 | 
						|
			aty := atys[k]
 | 
						|
			if i > 0 {
 | 
						|
				b.WriteRune(',')
 | 
						|
			}
 | 
						|
			av := val.GetAttr(k)
 | 
						|
			path[len(path)-1] = cty.GetAttrStep{
 | 
						|
				Name: k,
 | 
						|
			}
 | 
						|
			var err error
 | 
						|
			err = marshal(cty.StringVal(k), cty.String, path, b)
 | 
						|
			if err != nil {
 | 
						|
				return err
 | 
						|
			}
 | 
						|
			b.WriteRune(':')
 | 
						|
			err = marshal(av, aty, path, b)
 | 
						|
			if err != nil {
 | 
						|
				return err
 | 
						|
			}
 | 
						|
		}
 | 
						|
		b.WriteRune('}')
 | 
						|
		return nil
 | 
						|
	case t.IsCapsuleType():
 | 
						|
		rawVal := val.EncapsulatedValue()
 | 
						|
		jsonVal, err := json.Marshal(rawVal)
 | 
						|
		if err != nil {
 | 
						|
			return path.NewError(err)
 | 
						|
		}
 | 
						|
		b.Write(jsonVal)
 | 
						|
		return nil
 | 
						|
	default:
 | 
						|
		// should never happen
 | 
						|
		return path.NewErrorf("cannot JSON-serialize %s", t.FriendlyName())
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// marshalDynamic adds an extra wrapping object containing dynamic type
 | 
						|
// information for the given value.
 | 
						|
func marshalDynamic(val cty.Value, path cty.Path, b *bytes.Buffer) error {
 | 
						|
	typeJSON, err := MarshalType(val.Type())
 | 
						|
	if err != nil {
 | 
						|
		return path.NewErrorf("failed to serialize type: %s", err)
 | 
						|
	}
 | 
						|
	b.WriteString(`{"value":`)
 | 
						|
	marshal(val, val.Type(), path, b)
 | 
						|
	b.WriteString(`,"type":`)
 | 
						|
	b.Write(typeJSON)
 | 
						|
	b.WriteRune('}')
 | 
						|
	return nil
 | 
						|
}
 |