auto-update-daily-20260202
  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 implements YAML support for the Go language.
 17//
 18// Source code and other details for the project are available at GitHub:
 19//
 20//	https://github.com/go-yaml/yaml
 21package yaml
 22
 23import (
 24	"errors"
 25	"fmt"
 26	"io"
 27	"reflect"
 28	"strings"
 29	"sync"
 30	"unicode/utf8"
 31)
 32
 33// The Unmarshaler interface may be implemented by types to customize their
 34// behavior when being unmarshaled from a YAML document.
 35type Unmarshaler interface {
 36	UnmarshalYAML(value *Node) error
 37}
 38
 39type obsoleteUnmarshaler interface {
 40	UnmarshalYAML(unmarshal func(interface{}) error) error
 41}
 42
 43// The Marshaler interface may be implemented by types to customize their
 44// behavior when being marshaled into a YAML document. The returned value
 45// is marshaled in place of the original value implementing Marshaler.
 46//
 47// If an error is returned by MarshalYAML, the marshaling procedure stops
 48// and returns with the provided error.
 49type Marshaler interface {
 50	MarshalYAML() (interface{}, error)
 51}
 52
 53// Unmarshal decodes the first document found within the in byte slice
 54// and assigns decoded values into the out value.
 55//
 56// Maps and pointers (to a struct, string, int, etc) are accepted as out
 57// values. If an internal pointer within a struct is not initialized,
 58// the yaml package will initialize it if necessary for unmarshalling
 59// the provided data. The out parameter must not be nil.
 60//
 61// The type of the decoded values should be compatible with the respective
 62// values in out. If one or more values cannot be decoded due to a type
 63// mismatches, decoding continues partially until the end of the YAML
 64// content, and a *yaml.TypeError is returned with details for all
 65// missed values.
 66//
 67// Struct fields are only unmarshalled if they are exported (have an
 68// upper case first letter), and are unmarshalled using the field name
 69// lowercased as the default key. Custom keys may be defined via the
 70// "yaml" name in the field tag: the content preceding the first comma
 71// is used as the key, and the following comma-separated options are
 72// used to tweak the marshalling process (see Marshal).
 73// Conflicting names result in a runtime error.
 74//
 75// For example:
 76//
 77//	type T struct {
 78//	    F int `yaml:"a,omitempty"`
 79//	    B int
 80//	}
 81//	var t T
 82//	yaml.Unmarshal([]byte("a: 1\nb: 2"), &t)
 83//
 84// See the documentation of Marshal for the format of tags and a list of
 85// supported tag options.
 86func Unmarshal(in []byte, out interface{}) (err error) {
 87	return unmarshal(in, out, false)
 88}
 89
 90// A Decoder reads and decodes YAML values from an input stream.
 91type Decoder struct {
 92	parser      *parser
 93	knownFields bool
 94}
 95
 96// NewDecoder returns a new decoder that reads from r.
 97//
 98// The decoder introduces its own buffering and may read
 99// data from r beyond the YAML values requested.
100func NewDecoder(r io.Reader) *Decoder {
101	return &Decoder{
102		parser: newParserFromReader(r),
103	}
104}
105
106// KnownFields ensures that the keys in decoded mappings to
107// exist as fields in the struct being decoded into.
108func (dec *Decoder) KnownFields(enable bool) {
109	dec.knownFields = enable
110}
111
112// Decode reads the next YAML-encoded value from its input
113// and stores it in the value pointed to by v.
114//
115// See the documentation for Unmarshal for details about the
116// conversion of YAML into a Go value.
117func (dec *Decoder) Decode(v interface{}) (err error) {
118	d := newDecoder()
119	d.knownFields = dec.knownFields
120	defer handleErr(&err)
121	node := dec.parser.parse()
122	if node == nil {
123		return io.EOF
124	}
125	out := reflect.ValueOf(v)
126	if out.Kind() == reflect.Ptr && !out.IsNil() {
127		out = out.Elem()
128	}
129	d.unmarshal(node, out)
130	if len(d.terrors) > 0 {
131		return &TypeError{d.terrors}
132	}
133	return nil
134}
135
136// Decode decodes the node and stores its data into the value pointed to by v.
137//
138// See the documentation for Unmarshal for details about the
139// conversion of YAML into a Go value.
140func (n *Node) Decode(v interface{}) (err error) {
141	d := newDecoder()
142	defer handleErr(&err)
143	out := reflect.ValueOf(v)
144	if out.Kind() == reflect.Ptr && !out.IsNil() {
145		out = out.Elem()
146	}
147	d.unmarshal(n, out)
148	if len(d.terrors) > 0 {
149		return &TypeError{d.terrors}
150	}
151	return nil
152}
153
154func unmarshal(in []byte, out interface{}, strict bool) (err error) {
155	defer handleErr(&err)
156	d := newDecoder()
157	p := newParser(in)
158	defer p.destroy()
159	node := p.parse()
160	if node != nil {
161		v := reflect.ValueOf(out)
162		if v.Kind() == reflect.Ptr && !v.IsNil() {
163			v = v.Elem()
164		}
165		d.unmarshal(node, v)
166	}
167	if len(d.terrors) > 0 {
168		return &TypeError{d.terrors}
169	}
170	return nil
171}
172
173// Marshal serializes the value provided into a YAML document. The structure
174// of the generated document will reflect the structure of the value itself.
175// Maps and pointers (to struct, string, int, etc) are accepted as the in value.
176//
177// Struct fields are only marshalled if they are exported (have an upper case
178// first letter), and are marshalled using the field name lowercased as the
179// default key. Custom keys may be defined via the "yaml" name in the field
180// tag: the content preceding the first comma is used as the key, and the
181// following comma-separated options are used to tweak the marshalling process.
182// Conflicting names result in a runtime error.
183//
184// The field tag format accepted is:
185//
186//	`(...) yaml:"[<key>][,<flag1>[,<flag2>]]" (...)`
187//
188// The following flags are currently supported:
189//
190//	omitempty    Only include the field if it's not set to the zero
191//	             value for the type or to empty slices or maps.
192//	             Zero valued structs will be omitted if all their public
193//	             fields are zero, unless they implement an IsZero
194//	             method (see the IsZeroer interface type), in which
195//	             case the field will be excluded if IsZero returns true.
196//
197//	flow         Marshal using a flow style (useful for structs,
198//	             sequences and maps).
199//
200//	inline       Inline the field, which must be a struct or a map,
201//	             causing all of its fields or keys to be processed as if
202//	             they were part of the outer struct. For maps, keys must
203//	             not conflict with the yaml keys of other struct fields.
204//
205// In addition, if the key is "-", the field is ignored.
206//
207// For example:
208//
209//	type T struct {
210//	    F int `yaml:"a,omitempty"`
211//	    B int
212//	}
213//	yaml.Marshal(&T{B: 2}) // Returns "b: 2\n"
214//	yaml.Marshal(&T{F: 1}} // Returns "a: 1\nb: 0\n"
215func Marshal(in interface{}) (out []byte, err error) {
216	defer handleErr(&err)
217	e := newEncoder()
218	defer e.destroy()
219	e.marshalDoc("", reflect.ValueOf(in))
220	e.finish()
221	out = e.out
222	return
223}
224
225// An Encoder writes YAML values to an output stream.
226type Encoder struct {
227	encoder *encoder
228}
229
230// NewEncoder returns a new encoder that writes to w.
231// The Encoder should be closed after use to flush all data
232// to w.
233func NewEncoder(w io.Writer) *Encoder {
234	return &Encoder{
235		encoder: newEncoderWithWriter(w),
236	}
237}
238
239// Encode writes the YAML encoding of v to the stream.
240// If multiple items are encoded to the stream, the
241// second and subsequent document will be preceded
242// with a "---" document separator, but the first will not.
243//
244// See the documentation for Marshal for details about the conversion of Go
245// values to YAML.
246func (e *Encoder) Encode(v interface{}) (err error) {
247	defer handleErr(&err)
248	e.encoder.marshalDoc("", reflect.ValueOf(v))
249	return nil
250}
251
252// Encode encodes value v and stores its representation in n.
253//
254// See the documentation for Marshal for details about the
255// conversion of Go values into YAML.
256func (n *Node) Encode(v interface{}) (err error) {
257	defer handleErr(&err)
258	e := newEncoder()
259	defer e.destroy()
260	e.marshalDoc("", reflect.ValueOf(v))
261	e.finish()
262	p := newParser(e.out)
263	p.textless = true
264	defer p.destroy()
265	doc := p.parse()
266	*n = *doc.Content[0]
267	return nil
268}
269
270// SetIndent changes the used indentation used when encoding.
271func (e *Encoder) SetIndent(spaces int) {
272	if spaces < 0 {
273		panic("yaml: cannot indent to a negative number of spaces")
274	}
275	e.encoder.indent = spaces
276}
277
278// Close closes the encoder by writing any remaining data.
279// It does not write a stream terminating string "...".
280func (e *Encoder) Close() (err error) {
281	defer handleErr(&err)
282	e.encoder.finish()
283	return nil
284}
285
286func handleErr(err *error) {
287	if v := recover(); v != nil {
288		if e, ok := v.(yamlError); ok {
289			*err = e.err
290		} else {
291			panic(v)
292		}
293	}
294}
295
296type yamlError struct {
297	err error
298}
299
300func fail(err error) {
301	panic(yamlError{err})
302}
303
304func failf(format string, args ...interface{}) {
305	panic(yamlError{fmt.Errorf("yaml: "+format, args...)})
306}
307
308// A TypeError is returned by Unmarshal when one or more fields in
309// the YAML document cannot be properly decoded into the requested
310// types. When this error is returned, the value is still
311// unmarshaled partially.
312type TypeError struct {
313	Errors []string
314}
315
316func (e *TypeError) Error() string {
317	return fmt.Sprintf("yaml: unmarshal errors:\n  %s", strings.Join(e.Errors, "\n  "))
318}
319
320type Kind uint32
321
322const (
323	DocumentNode Kind = 1 << iota
324	SequenceNode
325	MappingNode
326	ScalarNode
327	AliasNode
328)
329
330type Style uint32
331
332const (
333	TaggedStyle Style = 1 << iota
334	DoubleQuotedStyle
335	SingleQuotedStyle
336	LiteralStyle
337	FoldedStyle
338	FlowStyle
339)
340
341// Node represents an element in the YAML document hierarchy. While documents
342// are typically encoded and decoded into higher level types, such as structs
343// and maps, Node is an intermediate representation that allows detailed
344// control over the content being decoded or encoded.
345//
346// It's worth noting that although Node offers access into details such as
347// line numbers, colums, and comments, the content when re-encoded will not
348// have its original textual representation preserved. An effort is made to
349// render the data plesantly, and to preserve comments near the data they
350// describe, though.
351//
352// Values that make use of the Node type interact with the yaml package in the
353// same way any other type would do, by encoding and decoding yaml data
354// directly or indirectly into them.
355//
356// For example:
357//
358//	var person struct {
359//	        Name    string
360//	        Address yaml.Node
361//	}
362//	err := yaml.Unmarshal(data, &person)
363//
364// Or by itself:
365//
366//	var person Node
367//	err := yaml.Unmarshal(data, &person)
368type Node struct {
369	// Kind defines whether the node is a document, a mapping, a sequence,
370	// a scalar value, or an alias to another node. The specific data type of
371	// scalar nodes may be obtained via the ShortTag and LongTag methods.
372	Kind Kind
373
374	// Style allows customizing the apperance of the node in the tree.
375	Style Style
376
377	// Tag holds the YAML tag defining the data type for the value.
378	// When decoding, this field will always be set to the resolved tag,
379	// even when it wasn't explicitly provided in the YAML content.
380	// When encoding, if this field is unset the value type will be
381	// implied from the node properties, and if it is set, it will only
382	// be serialized into the representation if TaggedStyle is used or
383	// the implicit tag diverges from the provided one.
384	Tag string
385
386	// Value holds the unescaped and unquoted represenation of the value.
387	Value string
388
389	// Anchor holds the anchor name for this node, which allows aliases to point to it.
390	Anchor string
391
392	// Alias holds the node that this alias points to. Only valid when Kind is AliasNode.
393	Alias *Node
394
395	// Content holds contained nodes for documents, mappings, and sequences.
396	Content []*Node
397
398	// HeadComment holds any comments in the lines preceding the node and
399	// not separated by an empty line.
400	HeadComment string
401
402	// LineComment holds any comments at the end of the line where the node is in.
403	LineComment string
404
405	// FootComment holds any comments following the node and before empty lines.
406	FootComment string
407
408	// Line and Column hold the node position in the decoded YAML text.
409	// These fields are not respected when encoding the node.
410	Line   int
411	Column int
412}
413
414// IsZero returns whether the node has all of its fields unset.
415func (n *Node) IsZero() bool {
416	return n.Kind == 0 && n.Style == 0 && n.Tag == "" && n.Value == "" && n.Anchor == "" && n.Alias == nil && n.Content == nil &&
417		n.HeadComment == "" && n.LineComment == "" && n.FootComment == "" && n.Line == 0 && n.Column == 0
418}
419
420// LongTag returns the long form of the tag that indicates the data type for
421// the node. If the Tag field isn't explicitly defined, one will be computed
422// based on the node properties.
423func (n *Node) LongTag() string {
424	return longTag(n.ShortTag())
425}
426
427// ShortTag returns the short form of the YAML tag that indicates data type for
428// the node. If the Tag field isn't explicitly defined, one will be computed
429// based on the node properties.
430func (n *Node) ShortTag() string {
431	if n.indicatedString() {
432		return strTag
433	}
434	if n.Tag == "" || n.Tag == "!" {
435		switch n.Kind {
436		case MappingNode:
437			return mapTag
438		case SequenceNode:
439			return seqTag
440		case AliasNode:
441			if n.Alias != nil {
442				return n.Alias.ShortTag()
443			}
444		case ScalarNode:
445			tag, _ := resolve("", n.Value)
446			return tag
447		case 0:
448			// Special case to make the zero value convenient.
449			if n.IsZero() {
450				return nullTag
451			}
452		}
453		return ""
454	}
455	return shortTag(n.Tag)
456}
457
458func (n *Node) indicatedString() bool {
459	return n.Kind == ScalarNode &&
460		(shortTag(n.Tag) == strTag ||
461			(n.Tag == "" || n.Tag == "!") && n.Style&(SingleQuotedStyle|DoubleQuotedStyle|LiteralStyle|FoldedStyle) != 0)
462}
463
464// SetString is a convenience function that sets the node to a string value
465// and defines its style in a pleasant way depending on its content.
466func (n *Node) SetString(s string) {
467	n.Kind = ScalarNode
468	if utf8.ValidString(s) {
469		n.Value = s
470		n.Tag = strTag
471	} else {
472		n.Value = encodeBase64(s)
473		n.Tag = binaryTag
474	}
475	if strings.Contains(n.Value, "\n") {
476		n.Style = LiteralStyle
477	}
478}
479
480// --------------------------------------------------------------------------
481// Maintain a mapping of keys to structure field indexes
482
483// The code in this section was copied from mgo/bson.
484
485// structInfo holds details for the serialization of fields of
486// a given struct.
487type structInfo struct {
488	FieldsMap  map[string]fieldInfo
489	FieldsList []fieldInfo
490
491	// InlineMap is the number of the field in the struct that
492	// contains an ,inline map, or -1 if there's none.
493	InlineMap int
494
495	// InlineUnmarshalers holds indexes to inlined fields that
496	// contain unmarshaler values.
497	InlineUnmarshalers [][]int
498}
499
500type fieldInfo struct {
501	Key       string
502	Num       int
503	OmitEmpty bool
504	Flow      bool
505	// Id holds the unique field identifier, so we can cheaply
506	// check for field duplicates without maintaining an extra map.
507	Id int
508
509	// Inline holds the field index if the field is part of an inlined struct.
510	Inline []int
511}
512
513var structMap = make(map[reflect.Type]*structInfo)
514var fieldMapMutex sync.RWMutex
515var unmarshalerType reflect.Type
516
517func init() {
518	var v Unmarshaler
519	unmarshalerType = reflect.ValueOf(&v).Elem().Type()
520}
521
522func getStructInfo(st reflect.Type) (*structInfo, error) {
523	fieldMapMutex.RLock()
524	sinfo, found := structMap[st]
525	fieldMapMutex.RUnlock()
526	if found {
527		return sinfo, nil
528	}
529
530	n := st.NumField()
531	fieldsMap := make(map[string]fieldInfo)
532	fieldsList := make([]fieldInfo, 0, n)
533	inlineMap := -1
534	inlineUnmarshalers := [][]int(nil)
535	for i := 0; i != n; i++ {
536		field := st.Field(i)
537		if field.PkgPath != "" && !field.Anonymous {
538			continue // Private field
539		}
540
541		info := fieldInfo{Num: i}
542
543		tag := field.Tag.Get("yaml")
544		if tag == "" && strings.Index(string(field.Tag), ":") < 0 {
545			tag = string(field.Tag)
546		}
547		if tag == "-" {
548			continue
549		}
550
551		inline := false
552		fields := strings.Split(tag, ",")
553		if len(fields) > 1 {
554			for _, flag := range fields[1:] {
555				switch flag {
556				case "omitempty":
557					info.OmitEmpty = true
558				case "flow":
559					info.Flow = true
560				case "inline":
561					inline = true
562				default:
563					return nil, errors.New(fmt.Sprintf("unsupported flag %q in tag %q of type %s", flag, tag, st))
564				}
565			}
566			tag = fields[0]
567		}
568
569		if inline {
570			switch field.Type.Kind() {
571			case reflect.Map:
572				if inlineMap >= 0 {
573					return nil, errors.New("multiple ,inline maps in struct " + st.String())
574				}
575				if field.Type.Key() != reflect.TypeOf("") {
576					return nil, errors.New("option ,inline needs a map with string keys in struct " + st.String())
577				}
578				inlineMap = info.Num
579			case reflect.Struct, reflect.Ptr:
580				ftype := field.Type
581				for ftype.Kind() == reflect.Ptr {
582					ftype = ftype.Elem()
583				}
584				if ftype.Kind() != reflect.Struct {
585					return nil, errors.New("option ,inline may only be used on a struct or map field")
586				}
587				if reflect.PtrTo(ftype).Implements(unmarshalerType) {
588					inlineUnmarshalers = append(inlineUnmarshalers, []int{i})
589				} else {
590					sinfo, err := getStructInfo(ftype)
591					if err != nil {
592						return nil, err
593					}
594					for _, index := range sinfo.InlineUnmarshalers {
595						inlineUnmarshalers = append(inlineUnmarshalers, append([]int{i}, index...))
596					}
597					for _, finfo := range sinfo.FieldsList {
598						if _, found := fieldsMap[finfo.Key]; found {
599							msg := "duplicated key '" + finfo.Key + "' in struct " + st.String()
600							return nil, errors.New(msg)
601						}
602						if finfo.Inline == nil {
603							finfo.Inline = []int{i, finfo.Num}
604						} else {
605							finfo.Inline = append([]int{i}, finfo.Inline...)
606						}
607						finfo.Id = len(fieldsList)
608						fieldsMap[finfo.Key] = finfo
609						fieldsList = append(fieldsList, finfo)
610					}
611				}
612			default:
613				return nil, errors.New("option ,inline may only be used on a struct or map field")
614			}
615			continue
616		}
617
618		if tag != "" {
619			info.Key = tag
620		} else {
621			info.Key = strings.ToLower(field.Name)
622		}
623
624		if _, found = fieldsMap[info.Key]; found {
625			msg := "duplicated key '" + info.Key + "' in struct " + st.String()
626			return nil, errors.New(msg)
627		}
628
629		info.Id = len(fieldsList)
630		fieldsList = append(fieldsList, info)
631		fieldsMap[info.Key] = info
632	}
633
634	sinfo = &structInfo{
635		FieldsMap:          fieldsMap,
636		FieldsList:         fieldsList,
637		InlineMap:          inlineMap,
638		InlineUnmarshalers: inlineUnmarshalers,
639	}
640
641	fieldMapMutex.Lock()
642	structMap[st] = sinfo
643	fieldMapMutex.Unlock()
644	return sinfo, nil
645}
646
647// IsZeroer is used to check whether an object is zero to
648// determine whether it should be omitted when marshaling
649// with the omitempty flag. One notable implementation
650// is time.Time.
651type IsZeroer interface {
652	IsZero() bool
653}
654
655func isZero(v reflect.Value) bool {
656	kind := v.Kind()
657	if z, ok := v.Interface().(IsZeroer); ok {
658		if (kind == reflect.Ptr || kind == reflect.Interface) && v.IsNil() {
659			return true
660		}
661		return z.IsZero()
662	}
663	switch kind {
664	case reflect.String:
665		return len(v.String()) == 0
666	case reflect.Interface, reflect.Ptr:
667		return v.IsNil()
668	case reflect.Slice:
669		return v.Len() == 0
670	case reflect.Map:
671		return v.Len() == 0
672	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
673		return v.Int() == 0
674	case reflect.Float32, reflect.Float64:
675		return v.Float() == 0
676	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
677		return v.Uint() == 0
678	case reflect.Bool:
679		return !v.Bool()
680	case reflect.Struct:
681		vt := v.Type()
682		for i := v.NumField() - 1; i >= 0; i-- {
683			if vt.Field(i).PkgPath != "" {
684				continue // Private field
685			}
686			if !isZero(v.Field(i)) {
687				return false
688			}
689		}
690		return true
691	}
692	return false
693}