package cty import ( "bytes" "encoding/json" "fmt" ) // MarshalJSON is an implementation of json.Marshaler that allows Type // instances to be serialized as JSON. // // All standard types can be serialized, but capsule types cannot since there // is no way to automatically recover the original pointer and capsule types // compare by equality. func (t Type) MarshalJSON() ([]byte, error) { switch impl := t.typeImpl.(type) { case primitiveType: switch impl.Kind { case primitiveTypeBool: return []byte{'"', 'b', 'o', 'o', 'l', '"'}, nil case primitiveTypeNumber: return []byte{'"', 'n', 'u', 'm', 'b', 'e', 'r', '"'}, nil case primitiveTypeString: return []byte{'"', 's', 't', 'r', 'i', 'n', 'g', '"'}, nil default: panic("unknown primitive type kind") } case typeList, typeMap, typeSet: buf := &bytes.Buffer{} etyJSON, err := t.ElementType().MarshalJSON() if err != nil { return nil, err } buf.WriteRune('[') switch impl.(type) { case typeList: buf.WriteString(`"list"`) case typeMap: buf.WriteString(`"map"`) case typeSet: buf.WriteString(`"set"`) } buf.WriteRune(',') buf.Write(etyJSON) buf.WriteRune(']') return buf.Bytes(), nil case typeObject: buf := &bytes.Buffer{} atysJSON, err := json.Marshal(t.AttributeTypes()) if err != nil { return nil, err } buf.WriteString(`["object",`) buf.Write(atysJSON) buf.WriteRune(']') return buf.Bytes(), nil case typeTuple: buf := &bytes.Buffer{} etysJSON, err := json.Marshal(t.TupleElementTypes()) if err != nil { return nil, err } buf.WriteString(`["tuple",`) buf.Write(etysJSON) buf.WriteRune(']') return buf.Bytes(), nil case pseudoTypeDynamic: return []byte{'"', 'd', 'y', 'n', 'a', 'm', 'i', 'c', '"'}, nil case *capsuleType: return nil, fmt.Errorf("type not allowed: %s", t.FriendlyName()) default: // should never happen panic("unknown type implementation") } } // UnmarshalJSON is the opposite of MarshalJSON. See the documentation of // MarshalJSON for information on the limitations of JSON serialization of // types. func (t *Type) UnmarshalJSON(buf []byte) error { r := bytes.NewReader(buf) dec := json.NewDecoder(r) tok, err := dec.Token() if err != nil { return err } switch v := tok.(type) { case string: switch v { case "bool": *t = Bool case "number": *t = Number case "string": *t = String case "dynamic": *t = DynamicPseudoType default: return fmt.Errorf("invalid primitive type name %q", v) } if dec.More() { return fmt.Errorf("extraneous data after type description") } return nil case json.Delim: if rune(v) != '[' { return fmt.Errorf("invalid complex type description") } tok, err = dec.Token() if err != nil { return err } kind, ok := tok.(string) if !ok { return fmt.Errorf("invalid complex type kind name") } switch kind { case "list": var ety Type err = dec.Decode(&ety) if err != nil { return err } *t = List(ety) case "map": var ety Type err = dec.Decode(&ety) if err != nil { return err } *t = Map(ety) case "set": var ety Type err = dec.Decode(&ety) if err != nil { return err } *t = Set(ety) case "object": var atys map[string]Type err = dec.Decode(&atys) if err != nil { return err } *t = Object(atys) case "tuple": var etys []Type err = dec.Decode(&etys) if err != nil { return err } *t = Tuple(etys) default: return fmt.Errorf("invalid complex type kind name") } tok, err = dec.Token() if err != nil { return err } if delim, ok := tok.(json.Delim); !ok || rune(delim) != ']' || dec.More() { return fmt.Errorf("unexpected extra data in type description") } return nil default: return fmt.Errorf("invalid type description") } }