| 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") |
| } |
| } |