]>
Commit | Line | Data |
---|---|---|
863486a6 AG |
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 | } |