Mercurial > yakumo_izuru > aya
comparison vendor/gopkg.in/yaml.v3/encode.go @ 74:d8727551f403 draft
The Empress (III)
* Change the way how versions are handled in version.go (to ease `go
install`)
* Upgrade yaml.v2 to yaml.v3
Signed-off-by: Izuru Yakumo <yakumo.izuru@chaotic.ninja>
author | yakumo.izuru |
---|---|
date | Mon, 04 Dec 2023 00:54:29 +0000 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
73:8533d875a2bb | 74:d8727551f403 |
---|---|
1 // | |
2 // Copyright (c) 2011-2019 Canonical Ltd | |
3 // | |
4 // Licensed under the Apache License, Version 2.0 (the "License"); | |
5 // you may not use this file except in compliance with the License. | |
6 // You may obtain a copy of the License at | |
7 // | |
8 // http://www.apache.org/licenses/LICENSE-2.0 | |
9 // | |
10 // Unless required by applicable law or agreed to in writing, software | |
11 // distributed under the License is distributed on an "AS IS" BASIS, | |
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 // See the License for the specific language governing permissions and | |
14 // limitations under the License. | |
15 | |
16 package yaml | |
17 | |
18 import ( | |
19 "encoding" | |
20 "fmt" | |
21 "io" | |
22 "reflect" | |
23 "regexp" | |
24 "sort" | |
25 "strconv" | |
26 "strings" | |
27 "time" | |
28 "unicode/utf8" | |
29 ) | |
30 | |
31 type encoder struct { | |
32 emitter yaml_emitter_t | |
33 event yaml_event_t | |
34 out []byte | |
35 flow bool | |
36 indent int | |
37 doneInit bool | |
38 } | |
39 | |
40 func newEncoder() *encoder { | |
41 e := &encoder{} | |
42 yaml_emitter_initialize(&e.emitter) | |
43 yaml_emitter_set_output_string(&e.emitter, &e.out) | |
44 yaml_emitter_set_unicode(&e.emitter, true) | |
45 return e | |
46 } | |
47 | |
48 func newEncoderWithWriter(w io.Writer) *encoder { | |
49 e := &encoder{} | |
50 yaml_emitter_initialize(&e.emitter) | |
51 yaml_emitter_set_output_writer(&e.emitter, w) | |
52 yaml_emitter_set_unicode(&e.emitter, true) | |
53 return e | |
54 } | |
55 | |
56 func (e *encoder) init() { | |
57 if e.doneInit { | |
58 return | |
59 } | |
60 if e.indent == 0 { | |
61 e.indent = 4 | |
62 } | |
63 e.emitter.best_indent = e.indent | |
64 yaml_stream_start_event_initialize(&e.event, yaml_UTF8_ENCODING) | |
65 e.emit() | |
66 e.doneInit = true | |
67 } | |
68 | |
69 func (e *encoder) finish() { | |
70 e.emitter.open_ended = false | |
71 yaml_stream_end_event_initialize(&e.event) | |
72 e.emit() | |
73 } | |
74 | |
75 func (e *encoder) destroy() { | |
76 yaml_emitter_delete(&e.emitter) | |
77 } | |
78 | |
79 func (e *encoder) emit() { | |
80 // This will internally delete the e.event value. | |
81 e.must(yaml_emitter_emit(&e.emitter, &e.event)) | |
82 } | |
83 | |
84 func (e *encoder) must(ok bool) { | |
85 if !ok { | |
86 msg := e.emitter.problem | |
87 if msg == "" { | |
88 msg = "unknown problem generating YAML content" | |
89 } | |
90 failf("%s", msg) | |
91 } | |
92 } | |
93 | |
94 func (e *encoder) marshalDoc(tag string, in reflect.Value) { | |
95 e.init() | |
96 var node *Node | |
97 if in.IsValid() { | |
98 node, _ = in.Interface().(*Node) | |
99 } | |
100 if node != nil && node.Kind == DocumentNode { | |
101 e.nodev(in) | |
102 } else { | |
103 yaml_document_start_event_initialize(&e.event, nil, nil, true) | |
104 e.emit() | |
105 e.marshal(tag, in) | |
106 yaml_document_end_event_initialize(&e.event, true) | |
107 e.emit() | |
108 } | |
109 } | |
110 | |
111 func (e *encoder) marshal(tag string, in reflect.Value) { | |
112 tag = shortTag(tag) | |
113 if !in.IsValid() || in.Kind() == reflect.Ptr && in.IsNil() { | |
114 e.nilv() | |
115 return | |
116 } | |
117 iface := in.Interface() | |
118 switch value := iface.(type) { | |
119 case *Node: | |
120 e.nodev(in) | |
121 return | |
122 case Node: | |
123 if !in.CanAddr() { | |
124 var n = reflect.New(in.Type()).Elem() | |
125 n.Set(in) | |
126 in = n | |
127 } | |
128 e.nodev(in.Addr()) | |
129 return | |
130 case time.Time: | |
131 e.timev(tag, in) | |
132 return | |
133 case *time.Time: | |
134 e.timev(tag, in.Elem()) | |
135 return | |
136 case time.Duration: | |
137 e.stringv(tag, reflect.ValueOf(value.String())) | |
138 return | |
139 case Marshaler: | |
140 v, err := value.MarshalYAML() | |
141 if err != nil { | |
142 fail(err) | |
143 } | |
144 if v == nil { | |
145 e.nilv() | |
146 return | |
147 } | |
148 e.marshal(tag, reflect.ValueOf(v)) | |
149 return | |
150 case encoding.TextMarshaler: | |
151 text, err := value.MarshalText() | |
152 if err != nil { | |
153 fail(err) | |
154 } | |
155 in = reflect.ValueOf(string(text)) | |
156 case nil: | |
157 e.nilv() | |
158 return | |
159 } | |
160 switch in.Kind() { | |
161 case reflect.Interface: | |
162 e.marshal(tag, in.Elem()) | |
163 case reflect.Map: | |
164 e.mapv(tag, in) | |
165 case reflect.Ptr: | |
166 e.marshal(tag, in.Elem()) | |
167 case reflect.Struct: | |
168 e.structv(tag, in) | |
169 case reflect.Slice, reflect.Array: | |
170 e.slicev(tag, in) | |
171 case reflect.String: | |
172 e.stringv(tag, in) | |
173 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: | |
174 e.intv(tag, in) | |
175 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: | |
176 e.uintv(tag, in) | |
177 case reflect.Float32, reflect.Float64: | |
178 e.floatv(tag, in) | |
179 case reflect.Bool: | |
180 e.boolv(tag, in) | |
181 default: | |
182 panic("cannot marshal type: " + in.Type().String()) | |
183 } | |
184 } | |
185 | |
186 func (e *encoder) mapv(tag string, in reflect.Value) { | |
187 e.mappingv(tag, func() { | |
188 keys := keyList(in.MapKeys()) | |
189 sort.Sort(keys) | |
190 for _, k := range keys { | |
191 e.marshal("", k) | |
192 e.marshal("", in.MapIndex(k)) | |
193 } | |
194 }) | |
195 } | |
196 | |
197 func (e *encoder) fieldByIndex(v reflect.Value, index []int) (field reflect.Value) { | |
198 for _, num := range index { | |
199 for { | |
200 if v.Kind() == reflect.Ptr { | |
201 if v.IsNil() { | |
202 return reflect.Value{} | |
203 } | |
204 v = v.Elem() | |
205 continue | |
206 } | |
207 break | |
208 } | |
209 v = v.Field(num) | |
210 } | |
211 return v | |
212 } | |
213 | |
214 func (e *encoder) structv(tag string, in reflect.Value) { | |
215 sinfo, err := getStructInfo(in.Type()) | |
216 if err != nil { | |
217 panic(err) | |
218 } | |
219 e.mappingv(tag, func() { | |
220 for _, info := range sinfo.FieldsList { | |
221 var value reflect.Value | |
222 if info.Inline == nil { | |
223 value = in.Field(info.Num) | |
224 } else { | |
225 value = e.fieldByIndex(in, info.Inline) | |
226 if !value.IsValid() { | |
227 continue | |
228 } | |
229 } | |
230 if info.OmitEmpty && isZero(value) { | |
231 continue | |
232 } | |
233 e.marshal("", reflect.ValueOf(info.Key)) | |
234 e.flow = info.Flow | |
235 e.marshal("", value) | |
236 } | |
237 if sinfo.InlineMap >= 0 { | |
238 m := in.Field(sinfo.InlineMap) | |
239 if m.Len() > 0 { | |
240 e.flow = false | |
241 keys := keyList(m.MapKeys()) | |
242 sort.Sort(keys) | |
243 for _, k := range keys { | |
244 if _, found := sinfo.FieldsMap[k.String()]; found { | |
245 panic(fmt.Sprintf("cannot have key %q in inlined map: conflicts with struct field", k.String())) | |
246 } | |
247 e.marshal("", k) | |
248 e.flow = false | |
249 e.marshal("", m.MapIndex(k)) | |
250 } | |
251 } | |
252 } | |
253 }) | |
254 } | |
255 | |
256 func (e *encoder) mappingv(tag string, f func()) { | |
257 implicit := tag == "" | |
258 style := yaml_BLOCK_MAPPING_STYLE | |
259 if e.flow { | |
260 e.flow = false | |
261 style = yaml_FLOW_MAPPING_STYLE | |
262 } | |
263 yaml_mapping_start_event_initialize(&e.event, nil, []byte(tag), implicit, style) | |
264 e.emit() | |
265 f() | |
266 yaml_mapping_end_event_initialize(&e.event) | |
267 e.emit() | |
268 } | |
269 | |
270 func (e *encoder) slicev(tag string, in reflect.Value) { | |
271 implicit := tag == "" | |
272 style := yaml_BLOCK_SEQUENCE_STYLE | |
273 if e.flow { | |
274 e.flow = false | |
275 style = yaml_FLOW_SEQUENCE_STYLE | |
276 } | |
277 e.must(yaml_sequence_start_event_initialize(&e.event, nil, []byte(tag), implicit, style)) | |
278 e.emit() | |
279 n := in.Len() | |
280 for i := 0; i < n; i++ { | |
281 e.marshal("", in.Index(i)) | |
282 } | |
283 e.must(yaml_sequence_end_event_initialize(&e.event)) | |
284 e.emit() | |
285 } | |
286 | |
287 // isBase60 returns whether s is in base 60 notation as defined in YAML 1.1. | |
288 // | |
289 // The base 60 float notation in YAML 1.1 is a terrible idea and is unsupported | |
290 // in YAML 1.2 and by this package, but these should be marshalled quoted for | |
291 // the time being for compatibility with other parsers. | |
292 func isBase60Float(s string) (result bool) { | |
293 // Fast path. | |
294 if s == "" { | |
295 return false | |
296 } | |
297 c := s[0] | |
298 if !(c == '+' || c == '-' || c >= '0' && c <= '9') || strings.IndexByte(s, ':') < 0 { | |
299 return false | |
300 } | |
301 // Do the full match. | |
302 return base60float.MatchString(s) | |
303 } | |
304 | |
305 // From http://yaml.org/type/float.html, except the regular expression there | |
306 // is bogus. In practice parsers do not enforce the "\.[0-9_]*" suffix. | |
307 var base60float = regexp.MustCompile(`^[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+(?:\.[0-9_]*)?$`) | |
308 | |
309 // isOldBool returns whether s is bool notation as defined in YAML 1.1. | |
310 // | |
311 // We continue to force strings that YAML 1.1 would interpret as booleans to be | |
312 // rendered as quotes strings so that the marshalled output valid for YAML 1.1 | |
313 // parsing. | |
314 func isOldBool(s string) (result bool) { | |
315 switch s { | |
316 case "y", "Y", "yes", "Yes", "YES", "on", "On", "ON", | |
317 "n", "N", "no", "No", "NO", "off", "Off", "OFF": | |
318 return true | |
319 default: | |
320 return false | |
321 } | |
322 } | |
323 | |
324 func (e *encoder) stringv(tag string, in reflect.Value) { | |
325 var style yaml_scalar_style_t | |
326 s := in.String() | |
327 canUsePlain := true | |
328 switch { | |
329 case !utf8.ValidString(s): | |
330 if tag == binaryTag { | |
331 failf("explicitly tagged !!binary data must be base64-encoded") | |
332 } | |
333 if tag != "" { | |
334 failf("cannot marshal invalid UTF-8 data as %s", shortTag(tag)) | |
335 } | |
336 // It can't be encoded directly as YAML so use a binary tag | |
337 // and encode it as base64. | |
338 tag = binaryTag | |
339 s = encodeBase64(s) | |
340 case tag == "": | |
341 // Check to see if it would resolve to a specific | |
342 // tag when encoded unquoted. If it doesn't, | |
343 // there's no need to quote it. | |
344 rtag, _ := resolve("", s) | |
345 canUsePlain = rtag == strTag && !(isBase60Float(s) || isOldBool(s)) | |
346 } | |
347 // Note: it's possible for user code to emit invalid YAML | |
348 // if they explicitly specify a tag and a string containing | |
349 // text that's incompatible with that tag. | |
350 switch { | |
351 case strings.Contains(s, "\n"): | |
352 if e.flow { | |
353 style = yaml_DOUBLE_QUOTED_SCALAR_STYLE | |
354 } else { | |
355 style = yaml_LITERAL_SCALAR_STYLE | |
356 } | |
357 case canUsePlain: | |
358 style = yaml_PLAIN_SCALAR_STYLE | |
359 default: | |
360 style = yaml_DOUBLE_QUOTED_SCALAR_STYLE | |
361 } | |
362 e.emitScalar(s, "", tag, style, nil, nil, nil, nil) | |
363 } | |
364 | |
365 func (e *encoder) boolv(tag string, in reflect.Value) { | |
366 var s string | |
367 if in.Bool() { | |
368 s = "true" | |
369 } else { | |
370 s = "false" | |
371 } | |
372 e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil) | |
373 } | |
374 | |
375 func (e *encoder) intv(tag string, in reflect.Value) { | |
376 s := strconv.FormatInt(in.Int(), 10) | |
377 e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil) | |
378 } | |
379 | |
380 func (e *encoder) uintv(tag string, in reflect.Value) { | |
381 s := strconv.FormatUint(in.Uint(), 10) | |
382 e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil) | |
383 } | |
384 | |
385 func (e *encoder) timev(tag string, in reflect.Value) { | |
386 t := in.Interface().(time.Time) | |
387 s := t.Format(time.RFC3339Nano) | |
388 e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil) | |
389 } | |
390 | |
391 func (e *encoder) floatv(tag string, in reflect.Value) { | |
392 // Issue #352: When formatting, use the precision of the underlying value | |
393 precision := 64 | |
394 if in.Kind() == reflect.Float32 { | |
395 precision = 32 | |
396 } | |
397 | |
398 s := strconv.FormatFloat(in.Float(), 'g', -1, precision) | |
399 switch s { | |
400 case "+Inf": | |
401 s = ".inf" | |
402 case "-Inf": | |
403 s = "-.inf" | |
404 case "NaN": | |
405 s = ".nan" | |
406 } | |
407 e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil) | |
408 } | |
409 | |
410 func (e *encoder) nilv() { | |
411 e.emitScalar("null", "", "", yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil) | |
412 } | |
413 | |
414 func (e *encoder) emitScalar(value, anchor, tag string, style yaml_scalar_style_t, head, line, foot, tail []byte) { | |
415 // TODO Kill this function. Replace all initialize calls by their underlining Go literals. | |
416 implicit := tag == "" | |
417 if !implicit { | |
418 tag = longTag(tag) | |
419 } | |
420 e.must(yaml_scalar_event_initialize(&e.event, []byte(anchor), []byte(tag), []byte(value), implicit, implicit, style)) | |
421 e.event.head_comment = head | |
422 e.event.line_comment = line | |
423 e.event.foot_comment = foot | |
424 e.event.tail_comment = tail | |
425 e.emit() | |
426 } | |
427 | |
428 func (e *encoder) nodev(in reflect.Value) { | |
429 e.node(in.Interface().(*Node), "") | |
430 } | |
431 | |
432 func (e *encoder) node(node *Node, tail string) { | |
433 // Zero nodes behave as nil. | |
434 if node.Kind == 0 && node.IsZero() { | |
435 e.nilv() | |
436 return | |
437 } | |
438 | |
439 // If the tag was not explicitly requested, and dropping it won't change the | |
440 // implicit tag of the value, don't include it in the presentation. | |
441 var tag = node.Tag | |
442 var stag = shortTag(tag) | |
443 var forceQuoting bool | |
444 if tag != "" && node.Style&TaggedStyle == 0 { | |
445 if node.Kind == ScalarNode { | |
446 if stag == strTag && node.Style&(SingleQuotedStyle|DoubleQuotedStyle|LiteralStyle|FoldedStyle) != 0 { | |
447 tag = "" | |
448 } else { | |
449 rtag, _ := resolve("", node.Value) | |
450 if rtag == stag { | |
451 tag = "" | |
452 } else if stag == strTag { | |
453 tag = "" | |
454 forceQuoting = true | |
455 } | |
456 } | |
457 } else { | |
458 var rtag string | |
459 switch node.Kind { | |
460 case MappingNode: | |
461 rtag = mapTag | |
462 case SequenceNode: | |
463 rtag = seqTag | |
464 } | |
465 if rtag == stag { | |
466 tag = "" | |
467 } | |
468 } | |
469 } | |
470 | |
471 switch node.Kind { | |
472 case DocumentNode: | |
473 yaml_document_start_event_initialize(&e.event, nil, nil, true) | |
474 e.event.head_comment = []byte(node.HeadComment) | |
475 e.emit() | |
476 for _, node := range node.Content { | |
477 e.node(node, "") | |
478 } | |
479 yaml_document_end_event_initialize(&e.event, true) | |
480 e.event.foot_comment = []byte(node.FootComment) | |
481 e.emit() | |
482 | |
483 case SequenceNode: | |
484 style := yaml_BLOCK_SEQUENCE_STYLE | |
485 if node.Style&FlowStyle != 0 { | |
486 style = yaml_FLOW_SEQUENCE_STYLE | |
487 } | |
488 e.must(yaml_sequence_start_event_initialize(&e.event, []byte(node.Anchor), []byte(longTag(tag)), tag == "", style)) | |
489 e.event.head_comment = []byte(node.HeadComment) | |
490 e.emit() | |
491 for _, node := range node.Content { | |
492 e.node(node, "") | |
493 } | |
494 e.must(yaml_sequence_end_event_initialize(&e.event)) | |
495 e.event.line_comment = []byte(node.LineComment) | |
496 e.event.foot_comment = []byte(node.FootComment) | |
497 e.emit() | |
498 | |
499 case MappingNode: | |
500 style := yaml_BLOCK_MAPPING_STYLE | |
501 if node.Style&FlowStyle != 0 { | |
502 style = yaml_FLOW_MAPPING_STYLE | |
503 } | |
504 yaml_mapping_start_event_initialize(&e.event, []byte(node.Anchor), []byte(longTag(tag)), tag == "", style) | |
505 e.event.tail_comment = []byte(tail) | |
506 e.event.head_comment = []byte(node.HeadComment) | |
507 e.emit() | |
508 | |
509 // The tail logic below moves the foot comment of prior keys to the following key, | |
510 // since the value for each key may be a nested structure and the foot needs to be | |
511 // processed only the entirety of the value is streamed. The last tail is processed | |
512 // with the mapping end event. | |
513 var tail string | |
514 for i := 0; i+1 < len(node.Content); i += 2 { | |
515 k := node.Content[i] | |
516 foot := k.FootComment | |
517 if foot != "" { | |
518 kopy := *k | |
519 kopy.FootComment = "" | |
520 k = &kopy | |
521 } | |
522 e.node(k, tail) | |
523 tail = foot | |
524 | |
525 v := node.Content[i+1] | |
526 e.node(v, "") | |
527 } | |
528 | |
529 yaml_mapping_end_event_initialize(&e.event) | |
530 e.event.tail_comment = []byte(tail) | |
531 e.event.line_comment = []byte(node.LineComment) | |
532 e.event.foot_comment = []byte(node.FootComment) | |
533 e.emit() | |
534 | |
535 case AliasNode: | |
536 yaml_alias_event_initialize(&e.event, []byte(node.Value)) | |
537 e.event.head_comment = []byte(node.HeadComment) | |
538 e.event.line_comment = []byte(node.LineComment) | |
539 e.event.foot_comment = []byte(node.FootComment) | |
540 e.emit() | |
541 | |
542 case ScalarNode: | |
543 value := node.Value | |
544 if !utf8.ValidString(value) { | |
545 if stag == binaryTag { | |
546 failf("explicitly tagged !!binary data must be base64-encoded") | |
547 } | |
548 if stag != "" { | |
549 failf("cannot marshal invalid UTF-8 data as %s", stag) | |
550 } | |
551 // It can't be encoded directly as YAML so use a binary tag | |
552 // and encode it as base64. | |
553 tag = binaryTag | |
554 value = encodeBase64(value) | |
555 } | |
556 | |
557 style := yaml_PLAIN_SCALAR_STYLE | |
558 switch { | |
559 case node.Style&DoubleQuotedStyle != 0: | |
560 style = yaml_DOUBLE_QUOTED_SCALAR_STYLE | |
561 case node.Style&SingleQuotedStyle != 0: | |
562 style = yaml_SINGLE_QUOTED_SCALAR_STYLE | |
563 case node.Style&LiteralStyle != 0: | |
564 style = yaml_LITERAL_SCALAR_STYLE | |
565 case node.Style&FoldedStyle != 0: | |
566 style = yaml_FOLDED_SCALAR_STYLE | |
567 case strings.Contains(value, "\n"): | |
568 style = yaml_LITERAL_SCALAR_STYLE | |
569 case forceQuoting: | |
570 style = yaml_DOUBLE_QUOTED_SCALAR_STYLE | |
571 } | |
572 | |
573 e.emitScalar(value, node.Anchor, tag, style, []byte(node.HeadComment), []byte(node.LineComment), []byte(node.FootComment), []byte(tail)) | |
574 default: | |
575 failf("cannot encode node with unknown kind %d", node.Kind) | |
576 } | |
577 } |