diff options
Diffstat (limited to 'vendor/github.com/zclconf/go-cty-yaml/implied_type.go')
-rw-r--r-- | vendor/github.com/zclconf/go-cty-yaml/implied_type.go | 268 |
1 files changed, 268 insertions, 0 deletions
diff --git a/vendor/github.com/zclconf/go-cty-yaml/implied_type.go b/vendor/github.com/zclconf/go-cty-yaml/implied_type.go new file mode 100644 index 0000000..5b7b068 --- /dev/null +++ b/vendor/github.com/zclconf/go-cty-yaml/implied_type.go | |||
@@ -0,0 +1,268 @@ | |||
1 | package yaml | ||
2 | |||
3 | import ( | ||
4 | "errors" | ||
5 | "fmt" | ||
6 | |||
7 | "github.com/zclconf/go-cty/cty" | ||
8 | "github.com/zclconf/go-cty/cty/convert" | ||
9 | ) | ||
10 | |||
11 | func (c *Converter) impliedType(src []byte) (cty.Type, error) { | ||
12 | p := &yaml_parser_t{} | ||
13 | if !yaml_parser_initialize(p) { | ||
14 | return cty.NilType, errors.New("failed to initialize YAML parser") | ||
15 | } | ||
16 | if len(src) == 0 { | ||
17 | src = []byte{'\n'} | ||
18 | } | ||
19 | |||
20 | an := &typeAnalysis{ | ||
21 | anchorsPending: map[string]int{}, | ||
22 | anchorTypes: map[string]cty.Type{}, | ||
23 | } | ||
24 | |||
25 | yaml_parser_set_input_string(p, src) | ||
26 | |||
27 | var evt yaml_event_t | ||
28 | if !yaml_parser_parse(p, &evt) { | ||
29 | return cty.NilType, parserError(p) | ||
30 | } | ||
31 | if evt.typ != yaml_STREAM_START_EVENT { | ||
32 | return cty.NilType, parseEventErrorf(&evt, "missing stream start token") | ||
33 | } | ||
34 | if !yaml_parser_parse(p, &evt) { | ||
35 | return cty.NilType, parserError(p) | ||
36 | } | ||
37 | if evt.typ != yaml_DOCUMENT_START_EVENT { | ||
38 | return cty.NilType, parseEventErrorf(&evt, "missing start of document") | ||
39 | } | ||
40 | |||
41 | ty, err := c.impliedTypeParse(an, p) | ||
42 | if err != nil { | ||
43 | return cty.NilType, err | ||
44 | } | ||
45 | |||
46 | if !yaml_parser_parse(p, &evt) { | ||
47 | return cty.NilType, parserError(p) | ||
48 | } | ||
49 | if evt.typ == yaml_DOCUMENT_START_EVENT { | ||
50 | return cty.NilType, parseEventErrorf(&evt, "only a single document is allowed") | ||
51 | } | ||
52 | if evt.typ != yaml_DOCUMENT_END_EVENT { | ||
53 | return cty.NilType, parseEventErrorf(&evt, "unexpected extra content (%s) after value", evt.typ.String()) | ||
54 | } | ||
55 | if !yaml_parser_parse(p, &evt) { | ||
56 | return cty.NilType, parserError(p) | ||
57 | } | ||
58 | if evt.typ != yaml_STREAM_END_EVENT { | ||
59 | return cty.NilType, parseEventErrorf(&evt, "unexpected extra content after value") | ||
60 | } | ||
61 | |||
62 | return ty, err | ||
63 | } | ||
64 | |||
65 | func (c *Converter) impliedTypeParse(an *typeAnalysis, p *yaml_parser_t) (cty.Type, error) { | ||
66 | var evt yaml_event_t | ||
67 | if !yaml_parser_parse(p, &evt) { | ||
68 | return cty.NilType, parserError(p) | ||
69 | } | ||
70 | return c.impliedTypeParseRemainder(an, &evt, p) | ||
71 | } | ||
72 | |||
73 | func (c *Converter) impliedTypeParseRemainder(an *typeAnalysis, evt *yaml_event_t, p *yaml_parser_t) (cty.Type, error) { | ||
74 | switch evt.typ { | ||
75 | case yaml_SCALAR_EVENT: | ||
76 | return c.impliedTypeScalar(an, evt, p) | ||
77 | case yaml_ALIAS_EVENT: | ||
78 | return c.impliedTypeAlias(an, evt, p) | ||
79 | case yaml_MAPPING_START_EVENT: | ||
80 | return c.impliedTypeMapping(an, evt, p) | ||
81 | case yaml_SEQUENCE_START_EVENT: | ||
82 | return c.impliedTypeSequence(an, evt, p) | ||
83 | case yaml_DOCUMENT_START_EVENT: | ||
84 | return cty.NilType, parseEventErrorf(evt, "only a single document is allowed") | ||
85 | case yaml_STREAM_END_EVENT: | ||
86 | // Decoding an empty buffer, probably | ||
87 | return cty.NilType, parseEventErrorf(evt, "expecting value but found end of stream") | ||
88 | default: | ||
89 | // Should never happen; the above should be comprehensive | ||
90 | return cty.NilType, parseEventErrorf(evt, "unexpected parser event %s", evt.typ.String()) | ||
91 | } | ||
92 | } | ||
93 | |||
94 | func (c *Converter) impliedTypeScalar(an *typeAnalysis, evt *yaml_event_t, p *yaml_parser_t) (cty.Type, error) { | ||
95 | src := evt.value | ||
96 | tag := string(evt.tag) | ||
97 | anchor := string(evt.anchor) | ||
98 | implicit := evt.implicit | ||
99 | |||
100 | if len(anchor) > 0 { | ||
101 | an.beginAnchor(anchor) | ||
102 | } | ||
103 | |||
104 | var ty cty.Type | ||
105 | switch { | ||
106 | case tag == "" && !implicit: | ||
107 | // Untagged explicit string | ||
108 | ty = cty.String | ||
109 | default: | ||
110 | v, err := c.resolveScalar(tag, string(src), yaml_scalar_style_t(evt.style)) | ||
111 | if err != nil { | ||
112 | return cty.NilType, parseEventErrorWrap(evt, err) | ||
113 | } | ||
114 | if v.RawEquals(mergeMappingVal) { | ||
115 | // In any context other than a mapping key, this is just a plain string | ||
116 | ty = cty.String | ||
117 | } else { | ||
118 | ty = v.Type() | ||
119 | } | ||
120 | } | ||
121 | |||
122 | if len(anchor) > 0 { | ||
123 | an.completeAnchor(anchor, ty) | ||
124 | } | ||
125 | return ty, nil | ||
126 | } | ||
127 | |||
128 | func (c *Converter) impliedTypeMapping(an *typeAnalysis, evt *yaml_event_t, p *yaml_parser_t) (cty.Type, error) { | ||
129 | tag := string(evt.tag) | ||
130 | anchor := string(evt.anchor) | ||
131 | |||
132 | if tag != "" && tag != yaml_MAP_TAG { | ||
133 | return cty.NilType, parseEventErrorf(evt, "can't interpret mapping as %s", tag) | ||
134 | } | ||
135 | |||
136 | if anchor != "" { | ||
137 | an.beginAnchor(anchor) | ||
138 | } | ||
139 | |||
140 | atys := make(map[string]cty.Type) | ||
141 | for { | ||
142 | var nextEvt yaml_event_t | ||
143 | if !yaml_parser_parse(p, &nextEvt) { | ||
144 | return cty.NilType, parserError(p) | ||
145 | } | ||
146 | if nextEvt.typ == yaml_MAPPING_END_EVENT { | ||
147 | ty := cty.Object(atys) | ||
148 | if anchor != "" { | ||
149 | an.completeAnchor(anchor, ty) | ||
150 | } | ||
151 | return ty, nil | ||
152 | } | ||
153 | |||
154 | if nextEvt.typ != yaml_SCALAR_EVENT { | ||
155 | return cty.NilType, parseEventErrorf(&nextEvt, "only strings are allowed as mapping keys") | ||
156 | } | ||
157 | keyVal, err := c.resolveScalar(string(nextEvt.tag), string(nextEvt.value), yaml_scalar_style_t(nextEvt.style)) | ||
158 | if err != nil { | ||
159 | return cty.NilType, err | ||
160 | } | ||
161 | if keyVal.RawEquals(mergeMappingVal) { | ||
162 | // Merging the value (which must be a mapping) into our mapping, | ||
163 | // then. | ||
164 | ty, err := c.impliedTypeParse(an, p) | ||
165 | if err != nil { | ||
166 | return cty.NilType, err | ||
167 | } | ||
168 | if !ty.IsObjectType() { | ||
169 | return cty.NilType, parseEventErrorf(&nextEvt, "cannot merge %s into mapping", ty.FriendlyName()) | ||
170 | } | ||
171 | for name, aty := range ty.AttributeTypes() { | ||
172 | atys[name] = aty | ||
173 | } | ||
174 | continue | ||
175 | } | ||
176 | if keyValStr, err := convert.Convert(keyVal, cty.String); err == nil { | ||
177 | keyVal = keyValStr | ||
178 | } else { | ||
179 | return cty.NilType, parseEventErrorf(&nextEvt, "only strings are allowed as mapping keys") | ||
180 | } | ||
181 | if keyVal.IsNull() { | ||
182 | return cty.NilType, parseEventErrorf(&nextEvt, "mapping key cannot be null") | ||
183 | } | ||
184 | if !keyVal.IsKnown() { | ||
185 | return cty.NilType, parseEventErrorf(&nextEvt, "mapping key must be known") | ||
186 | } | ||
187 | valTy, err := c.impliedTypeParse(an, p) | ||
188 | if err != nil { | ||
189 | return cty.NilType, err | ||
190 | } | ||
191 | |||
192 | atys[keyVal.AsString()] = valTy | ||
193 | } | ||
194 | } | ||
195 | |||
196 | func (c *Converter) impliedTypeSequence(an *typeAnalysis, evt *yaml_event_t, p *yaml_parser_t) (cty.Type, error) { | ||
197 | tag := string(evt.tag) | ||
198 | anchor := string(evt.anchor) | ||
199 | |||
200 | if tag != "" && tag != yaml_SEQ_TAG { | ||
201 | return cty.NilType, parseEventErrorf(evt, "can't interpret sequence as %s", tag) | ||
202 | } | ||
203 | |||
204 | if anchor != "" { | ||
205 | an.beginAnchor(anchor) | ||
206 | } | ||
207 | |||
208 | var atys []cty.Type | ||
209 | for { | ||
210 | var nextEvt yaml_event_t | ||
211 | if !yaml_parser_parse(p, &nextEvt) { | ||
212 | return cty.NilType, parserError(p) | ||
213 | } | ||
214 | if nextEvt.typ == yaml_SEQUENCE_END_EVENT { | ||
215 | ty := cty.Tuple(atys) | ||
216 | if anchor != "" { | ||
217 | an.completeAnchor(anchor, ty) | ||
218 | } | ||
219 | return ty, nil | ||
220 | } | ||
221 | |||
222 | valTy, err := c.impliedTypeParseRemainder(an, &nextEvt, p) | ||
223 | if err != nil { | ||
224 | return cty.NilType, err | ||
225 | } | ||
226 | |||
227 | atys = append(atys, valTy) | ||
228 | } | ||
229 | } | ||
230 | |||
231 | func (c *Converter) impliedTypeAlias(an *typeAnalysis, evt *yaml_event_t, p *yaml_parser_t) (cty.Type, error) { | ||
232 | ty, err := an.anchorType(string(evt.anchor)) | ||
233 | if err != nil { | ||
234 | err = parseEventErrorWrap(evt, err) | ||
235 | } | ||
236 | return ty, err | ||
237 | } | ||
238 | |||
239 | type typeAnalysis struct { | ||
240 | anchorsPending map[string]int | ||
241 | anchorTypes map[string]cty.Type | ||
242 | } | ||
243 | |||
244 | func (an *typeAnalysis) beginAnchor(name string) { | ||
245 | an.anchorsPending[name]++ | ||
246 | } | ||
247 | |||
248 | func (an *typeAnalysis) completeAnchor(name string, ty cty.Type) { | ||
249 | an.anchorsPending[name]-- | ||
250 | if an.anchorsPending[name] == 0 { | ||
251 | delete(an.anchorsPending, name) | ||
252 | } | ||
253 | an.anchorTypes[name] = ty | ||
254 | } | ||
255 | |||
256 | func (an *typeAnalysis) anchorType(name string) (cty.Type, error) { | ||
257 | if _, pending := an.anchorsPending[name]; pending { | ||
258 | // YAML normally allows self-referencing structures, but cty cannot | ||
259 | // represent them (it requires all structures to be finite) so we | ||
260 | // must fail here. | ||
261 | return cty.NilType, fmt.Errorf("cannot refer to anchor %q from inside its own definition", name) | ||
262 | } | ||
263 | ty, ok := an.anchorTypes[name] | ||
264 | if !ok { | ||
265 | return cty.NilType, fmt.Errorf("reference to undefined anchor %q", name) | ||
266 | } | ||
267 | return ty, nil | ||
268 | } | ||