]>
Commit | Line | Data |
---|---|---|
15c0b25d AP |
1 | package json |
2 | ||
3 | import ( | |
4 | "bytes" | |
5 | "encoding/json" | |
6 | "fmt" | |
7 | ||
8 | "github.com/zclconf/go-cty/cty" | |
9 | ) | |
10 | ||
11 | // ImpliedType returns the cty Type implied by the structure of the given | |
12 | // JSON-compliant buffer. This function implements the default type mapping | |
13 | // behavior used when decoding arbitrary JSON without explicit cty Type | |
14 | // information. | |
15 | // | |
16 | // The rules are as follows: | |
17 | // | |
18 | // JSON strings, numbers and bools map to their equivalent primitive type in | |
19 | // cty. | |
20 | // | |
21 | // JSON objects map to cty object types, with the attributes defined by the | |
22 | // object keys and the types of their values. | |
23 | // | |
24 | // JSON arrays map to cty tuple types, with the elements defined by the | |
25 | // types of the array members. | |
26 | // | |
27 | // Any nulls are typed as DynamicPseudoType, so callers of this function | |
28 | // must be prepared to deal with this. Callers that do not wish to deal with | |
29 | // dynamic typing should not use this function and should instead describe | |
30 | // their required types explicitly with a cty.Type instance when decoding. | |
31 | // | |
32 | // Any JSON syntax errors will be returned as an error, and the type will | |
33 | // be the invalid value cty.NilType. | |
34 | func ImpliedType(buf []byte) (cty.Type, error) { | |
35 | r := bytes.NewReader(buf) | |
36 | dec := json.NewDecoder(r) | |
37 | dec.UseNumber() | |
38 | ||
39 | ty, err := impliedType(dec) | |
40 | if err != nil { | |
41 | return cty.NilType, err | |
42 | } | |
43 | ||
44 | if dec.More() { | |
45 | return cty.NilType, fmt.Errorf("extraneous data after JSON object") | |
46 | } | |
47 | ||
48 | return ty, nil | |
49 | } | |
50 | ||
51 | func impliedType(dec *json.Decoder) (cty.Type, error) { | |
52 | tok, err := dec.Token() | |
53 | if err != nil { | |
54 | return cty.NilType, err | |
55 | } | |
56 | ||
57 | return impliedTypeForTok(tok, dec) | |
58 | } | |
59 | ||
60 | func impliedTypeForTok(tok json.Token, dec *json.Decoder) (cty.Type, error) { | |
61 | if tok == nil { | |
62 | return cty.DynamicPseudoType, nil | |
63 | } | |
64 | ||
65 | switch ttok := tok.(type) { | |
66 | case bool: | |
67 | return cty.Bool, nil | |
68 | ||
69 | case json.Number: | |
70 | return cty.Number, nil | |
71 | ||
72 | case string: | |
73 | return cty.String, nil | |
74 | ||
75 | case json.Delim: | |
76 | ||
77 | switch rune(ttok) { | |
78 | case '{': | |
79 | return impliedObjectType(dec) | |
80 | case '[': | |
81 | return impliedTupleType(dec) | |
82 | default: | |
83 | return cty.NilType, fmt.Errorf("unexpected token %q", ttok) | |
84 | } | |
85 | ||
86 | default: | |
87 | return cty.NilType, fmt.Errorf("unsupported JSON token %#v", tok) | |
88 | } | |
89 | } | |
90 | ||
91 | func impliedObjectType(dec *json.Decoder) (cty.Type, error) { | |
92 | // By the time we get in here, we've already consumed the { delimiter | |
93 | // and so our next token should be the first object key. | |
94 | ||
95 | var atys map[string]cty.Type | |
96 | ||
97 | for { | |
98 | // Read the object key first | |
99 | tok, err := dec.Token() | |
100 | if err != nil { | |
101 | return cty.NilType, err | |
102 | } | |
103 | ||
104 | if ttok, ok := tok.(json.Delim); ok { | |
105 | if rune(ttok) != '}' { | |
106 | return cty.NilType, fmt.Errorf("unexpected delimiter %q", ttok) | |
107 | } | |
108 | break | |
109 | } | |
110 | ||
111 | key, ok := tok.(string) | |
112 | if !ok { | |
113 | return cty.NilType, fmt.Errorf("expected string but found %T", tok) | |
114 | } | |
115 | ||
116 | // Now read the value | |
117 | tok, err = dec.Token() | |
118 | if err != nil { | |
119 | return cty.NilType, err | |
120 | } | |
121 | ||
122 | aty, err := impliedTypeForTok(tok, dec) | |
123 | if err != nil { | |
124 | return cty.NilType, err | |
125 | } | |
126 | ||
127 | if atys == nil { | |
128 | atys = make(map[string]cty.Type) | |
129 | } | |
130 | atys[key] = aty | |
131 | } | |
132 | ||
133 | if len(atys) == 0 { | |
134 | return cty.EmptyObject, nil | |
135 | } | |
136 | ||
137 | return cty.Object(atys), nil | |
138 | } | |
139 | ||
140 | func impliedTupleType(dec *json.Decoder) (cty.Type, error) { | |
107c1cdb | 141 | // By the time we get in here, we've already consumed the [ delimiter |
15c0b25d AP |
142 | // and so our next token should be the first value. |
143 | ||
144 | var etys []cty.Type | |
145 | ||
146 | for { | |
147 | tok, err := dec.Token() | |
148 | if err != nil { | |
149 | return cty.NilType, err | |
150 | } | |
151 | ||
152 | if ttok, ok := tok.(json.Delim); ok { | |
107c1cdb ND |
153 | if rune(ttok) == ']' { |
154 | break | |
15c0b25d | 155 | } |
15c0b25d AP |
156 | } |
157 | ||
158 | ety, err := impliedTypeForTok(tok, dec) | |
159 | if err != nil { | |
160 | return cty.NilType, err | |
161 | } | |
162 | etys = append(etys, ety) | |
163 | } | |
164 | ||
165 | if len(etys) == 0 { | |
166 | return cty.EmptyTuple, nil | |
167 | } | |
168 | ||
169 | return cty.Tuple(etys), nil | |
170 | } |