Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
kardolus
GitHub Repository: kardolus/chatgpt-cli
Path: blob/main/vendor/gopkg.in/yaml.v3/encode.go
2872 views
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
}
578
579