//1// Copyright (c) 2011-2019 Canonical Ltd2//3// Licensed under the Apache License, Version 2.0 (the "License");4// you may not use this file except in compliance with the License.5// You may obtain a copy of the License at6//7// http://www.apache.org/licenses/LICENSE-2.08//9// Unless required by applicable law or agreed to in writing, software10// distributed under the License is distributed on an "AS IS" BASIS,11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.12// See the License for the specific language governing permissions and13// limitations under the License.1415// Package yaml implements YAML support for the Go language.16//17// Source code and other details for the project are available at GitHub:18//19// https://github.com/yaml/go-yaml20package yaml2122import (23"errors"24"fmt"25"io"26"reflect"27"strings"28"sync"29"unicode/utf8"30)3132// The Unmarshaler interface may be implemented by types to customize their33// behavior when being unmarshaled from a YAML document.34type Unmarshaler interface {35UnmarshalYAML(value *Node) error36}3738type obsoleteUnmarshaler interface {39UnmarshalYAML(unmarshal func(interface{}) error) error40}4142// The Marshaler interface may be implemented by types to customize their43// behavior when being marshaled into a YAML document. The returned value44// is marshaled in place of the original value implementing Marshaler.45//46// If an error is returned by MarshalYAML, the marshaling procedure stops47// and returns with the provided error.48type Marshaler interface {49MarshalYAML() (interface{}, error)50}5152// Unmarshal decodes the first document found within the in byte slice53// and assigns decoded values into the out value.54//55// Maps and pointers (to a struct, string, int, etc) are accepted as out56// values. If an internal pointer within a struct is not initialized,57// the yaml package will initialize it if necessary for unmarshalling58// the provided data. The out parameter must not be nil.59//60// The type of the decoded values should be compatible with the respective61// values in out. If one or more values cannot be decoded due to a type62// mismatches, decoding continues partially until the end of the YAML63// content, and a *yaml.TypeError is returned with details for all64// missed values.65//66// Struct fields are only unmarshalled if they are exported (have an67// upper case first letter), and are unmarshalled using the field name68// lowercased as the default key. Custom keys may be defined via the69// "yaml" name in the field tag: the content preceding the first comma70// is used as the key, and the following comma-separated options are71// used to tweak the marshalling process (see Marshal).72// Conflicting names result in a runtime error.73//74// For example:75//76// type T struct {77// F int `yaml:"a,omitempty"`78// B int79// }80// var t T81// yaml.Unmarshal([]byte("a: 1\nb: 2"), &t)82//83// See the documentation of Marshal for the format of tags and a list of84// supported tag options.85func Unmarshal(in []byte, out interface{}) (err error) {86return unmarshal(in, out, false)87}8889// A Decoder reads and decodes YAML values from an input stream.90type Decoder struct {91parser *parser92knownFields bool93}9495// NewDecoder returns a new decoder that reads from r.96//97// The decoder introduces its own buffering and may read98// data from r beyond the YAML values requested.99func NewDecoder(r io.Reader) *Decoder {100return &Decoder{101parser: newParserFromReader(r),102}103}104105// KnownFields ensures that the keys in decoded mappings to106// exist as fields in the struct being decoded into.107func (dec *Decoder) KnownFields(enable bool) {108dec.knownFields = enable109}110111// Decode reads the next YAML-encoded value from its input112// and stores it in the value pointed to by v.113//114// See the documentation for Unmarshal for details about the115// conversion of YAML into a Go value.116func (dec *Decoder) Decode(v interface{}) (err error) {117d := newDecoder()118d.knownFields = dec.knownFields119defer handleErr(&err)120node := dec.parser.parse()121if node == nil {122return io.EOF123}124out := reflect.ValueOf(v)125if out.Kind() == reflect.Ptr && !out.IsNil() {126out = out.Elem()127}128d.unmarshal(node, out)129if len(d.terrors) > 0 {130return &TypeError{d.terrors}131}132return nil133}134135// Decode decodes the node and stores its data into the value pointed to by v.136//137// See the documentation for Unmarshal for details about the138// conversion of YAML into a Go value.139func (n *Node) Decode(v interface{}) (err error) {140d := newDecoder()141defer handleErr(&err)142out := reflect.ValueOf(v)143if out.Kind() == reflect.Ptr && !out.IsNil() {144out = out.Elem()145}146d.unmarshal(n, out)147if len(d.terrors) > 0 {148return &TypeError{d.terrors}149}150return nil151}152153func unmarshal(in []byte, out interface{}, strict bool) (err error) {154defer handleErr(&err)155d := newDecoder()156p := newParser(in)157defer p.destroy()158node := p.parse()159if node != nil {160v := reflect.ValueOf(out)161if v.Kind() == reflect.Ptr && !v.IsNil() {162v = v.Elem()163}164d.unmarshal(node, v)165}166if len(d.terrors) > 0 {167return &TypeError{d.terrors}168}169return nil170}171172// Marshal serializes the value provided into a YAML document. The structure173// of the generated document will reflect the structure of the value itself.174// Maps and pointers (to struct, string, int, etc) are accepted as the in value.175//176// Struct fields are only marshalled if they are exported (have an upper case177// first letter), and are marshalled using the field name lowercased as the178// default key. Custom keys may be defined via the "yaml" name in the field179// tag: the content preceding the first comma is used as the key, and the180// following comma-separated options are used to tweak the marshalling process.181// Conflicting names result in a runtime error.182//183// The field tag format accepted is:184//185// `(...) yaml:"[<key>][,<flag1>[,<flag2>]]" (...)`186//187// The following flags are currently supported:188//189// omitempty Only include the field if it's not set to the zero190// value for the type or to empty slices or maps.191// Zero valued structs will be omitted if all their public192// fields are zero, unless they implement an IsZero193// method (see the IsZeroer interface type), in which194// case the field will be excluded if IsZero returns true.195//196// flow Marshal using a flow style (useful for structs,197// sequences and maps).198//199// inline Inline the field, which must be a struct or a map,200// causing all of its fields or keys to be processed as if201// they were part of the outer struct. For maps, keys must202// not conflict with the yaml keys of other struct fields.203//204// In addition, if the key is "-", the field is ignored.205//206// For example:207//208// type T struct {209// F int `yaml:"a,omitempty"`210// B int211// }212// yaml.Marshal(&T{B: 2}) // Returns "b: 2\n"213// yaml.Marshal(&T{F: 1}} // Returns "a: 1\nb: 0\n"214func Marshal(in interface{}) (out []byte, err error) {215defer handleErr(&err)216e := newEncoder()217defer e.destroy()218e.marshalDoc("", reflect.ValueOf(in))219e.finish()220out = e.out221return222}223224// An Encoder writes YAML values to an output stream.225type Encoder struct {226encoder *encoder227}228229// NewEncoder returns a new encoder that writes to w.230// The Encoder should be closed after use to flush all data231// to w.232func NewEncoder(w io.Writer) *Encoder {233return &Encoder{234encoder: newEncoderWithWriter(w),235}236}237238// Encode writes the YAML encoding of v to the stream.239// If multiple items are encoded to the stream, the240// second and subsequent document will be preceded241// with a "---" document separator, but the first will not.242//243// See the documentation for Marshal for details about the conversion of Go244// values to YAML.245func (e *Encoder) Encode(v interface{}) (err error) {246defer handleErr(&err)247e.encoder.marshalDoc("", reflect.ValueOf(v))248return nil249}250251// Encode encodes value v and stores its representation in n.252//253// See the documentation for Marshal for details about the254// conversion of Go values into YAML.255func (n *Node) Encode(v interface{}) (err error) {256defer handleErr(&err)257e := newEncoder()258defer e.destroy()259e.marshalDoc("", reflect.ValueOf(v))260e.finish()261p := newParser(e.out)262p.textless = true263defer p.destroy()264doc := p.parse()265*n = *doc.Content[0]266return nil267}268269// SetIndent changes the used indentation used when encoding.270func (e *Encoder) SetIndent(spaces int) {271if spaces < 0 {272panic("yaml: cannot indent to a negative number of spaces")273}274e.encoder.indent = spaces275}276277// CompactSeqIndent makes it so that '- ' is considered part of the indentation.278func (e *Encoder) CompactSeqIndent() {279e.encoder.emitter.compact_sequence_indent = true280}281282// DefaultSeqIndent makes it so that '- ' is not considered part of the indentation.283func (e *Encoder) DefaultSeqIndent() {284e.encoder.emitter.compact_sequence_indent = false285}286287// Close closes the encoder by writing any remaining data.288// It does not write a stream terminating string "...".289func (e *Encoder) Close() (err error) {290defer handleErr(&err)291e.encoder.finish()292return nil293}294295func handleErr(err *error) {296if v := recover(); v != nil {297if e, ok := v.(yamlError); ok {298*err = e.err299} else {300panic(v)301}302}303}304305type yamlError struct {306err error307}308309func fail(err error) {310panic(yamlError{err})311}312313func failf(format string, args ...interface{}) {314panic(yamlError{fmt.Errorf("yaml: "+format, args...)})315}316317// A TypeError is returned by Unmarshal when one or more fields in318// the YAML document cannot be properly decoded into the requested319// types. When this error is returned, the value is still320// unmarshaled partially.321type TypeError struct {322Errors []string323}324325func (e *TypeError) Error() string {326return fmt.Sprintf("yaml: unmarshal errors:\n %s", strings.Join(e.Errors, "\n "))327}328329type Kind uint32330331const (332DocumentNode Kind = 1 << iota333SequenceNode334MappingNode335ScalarNode336AliasNode337)338339type Style uint32340341const (342TaggedStyle Style = 1 << iota343DoubleQuotedStyle344SingleQuotedStyle345LiteralStyle346FoldedStyle347FlowStyle348)349350// Node represents an element in the YAML document hierarchy. While documents351// are typically encoded and decoded into higher level types, such as structs352// and maps, Node is an intermediate representation that allows detailed353// control over the content being decoded or encoded.354//355// It's worth noting that although Node offers access into details such as356// line numbers, colums, and comments, the content when re-encoded will not357// have its original textual representation preserved. An effort is made to358// render the data plesantly, and to preserve comments near the data they359// describe, though.360//361// Values that make use of the Node type interact with the yaml package in the362// same way any other type would do, by encoding and decoding yaml data363// directly or indirectly into them.364//365// For example:366//367// var person struct {368// Name string369// Address yaml.Node370// }371// err := yaml.Unmarshal(data, &person)372//373// Or by itself:374//375// var person Node376// err := yaml.Unmarshal(data, &person)377type Node struct {378// Kind defines whether the node is a document, a mapping, a sequence,379// a scalar value, or an alias to another node. The specific data type of380// scalar nodes may be obtained via the ShortTag and LongTag methods.381Kind Kind382383// Style allows customizing the apperance of the node in the tree.384Style Style385386// Tag holds the YAML tag defining the data type for the value.387// When decoding, this field will always be set to the resolved tag,388// even when it wasn't explicitly provided in the YAML content.389// When encoding, if this field is unset the value type will be390// implied from the node properties, and if it is set, it will only391// be serialized into the representation if TaggedStyle is used or392// the implicit tag diverges from the provided one.393Tag string394395// Value holds the unescaped and unquoted represenation of the value.396Value string397398// Anchor holds the anchor name for this node, which allows aliases to point to it.399Anchor string400401// Alias holds the node that this alias points to. Only valid when Kind is AliasNode.402Alias *Node403404// Content holds contained nodes for documents, mappings, and sequences.405Content []*Node406407// HeadComment holds any comments in the lines preceding the node and408// not separated by an empty line.409HeadComment string410411// LineComment holds any comments at the end of the line where the node is in.412LineComment string413414// FootComment holds any comments following the node and before empty lines.415FootComment string416417// Line and Column hold the node position in the decoded YAML text.418// These fields are not respected when encoding the node.419Line int420Column int421}422423// IsZero returns whether the node has all of its fields unset.424func (n *Node) IsZero() bool {425return n.Kind == 0 && n.Style == 0 && n.Tag == "" && n.Value == "" && n.Anchor == "" && n.Alias == nil && n.Content == nil &&426n.HeadComment == "" && n.LineComment == "" && n.FootComment == "" && n.Line == 0 && n.Column == 0427}428429// LongTag returns the long form of the tag that indicates the data type for430// the node. If the Tag field isn't explicitly defined, one will be computed431// based on the node properties.432func (n *Node) LongTag() string {433return longTag(n.ShortTag())434}435436// ShortTag returns the short form of the YAML tag that indicates data type for437// the node. If the Tag field isn't explicitly defined, one will be computed438// based on the node properties.439func (n *Node) ShortTag() string {440if n.indicatedString() {441return strTag442}443if n.Tag == "" || n.Tag == "!" {444switch n.Kind {445case MappingNode:446return mapTag447case SequenceNode:448return seqTag449case AliasNode:450if n.Alias != nil {451return n.Alias.ShortTag()452}453case ScalarNode:454tag, _ := resolve("", n.Value)455return tag456case 0:457// Special case to make the zero value convenient.458if n.IsZero() {459return nullTag460}461}462return ""463}464return shortTag(n.Tag)465}466467func (n *Node) indicatedString() bool {468return n.Kind == ScalarNode &&469(shortTag(n.Tag) == strTag ||470(n.Tag == "" || n.Tag == "!") && n.Style&(SingleQuotedStyle|DoubleQuotedStyle|LiteralStyle|FoldedStyle) != 0)471}472473// SetString is a convenience function that sets the node to a string value474// and defines its style in a pleasant way depending on its content.475func (n *Node) SetString(s string) {476n.Kind = ScalarNode477if utf8.ValidString(s) {478n.Value = s479n.Tag = strTag480} else {481n.Value = encodeBase64(s)482n.Tag = binaryTag483}484if strings.Contains(n.Value, "\n") {485n.Style = LiteralStyle486}487}488489// --------------------------------------------------------------------------490// Maintain a mapping of keys to structure field indexes491492// The code in this section was copied from mgo/bson.493494// structInfo holds details for the serialization of fields of495// a given struct.496type structInfo struct {497FieldsMap map[string]fieldInfo498FieldsList []fieldInfo499500// InlineMap is the number of the field in the struct that501// contains an ,inline map, or -1 if there's none.502InlineMap int503504// InlineUnmarshalers holds indexes to inlined fields that505// contain unmarshaler values.506InlineUnmarshalers [][]int507}508509type fieldInfo struct {510Key string511Num int512OmitEmpty bool513Flow bool514// Id holds the unique field identifier, so we can cheaply515// check for field duplicates without maintaining an extra map.516Id int517518// Inline holds the field index if the field is part of an inlined struct.519Inline []int520}521522var structMap = make(map[reflect.Type]*structInfo)523var fieldMapMutex sync.RWMutex524var unmarshalerType reflect.Type525526func init() {527var v Unmarshaler528unmarshalerType = reflect.ValueOf(&v).Elem().Type()529}530531func getStructInfo(st reflect.Type) (*structInfo, error) {532fieldMapMutex.RLock()533sinfo, found := structMap[st]534fieldMapMutex.RUnlock()535if found {536return sinfo, nil537}538539n := st.NumField()540fieldsMap := make(map[string]fieldInfo)541fieldsList := make([]fieldInfo, 0, n)542inlineMap := -1543inlineUnmarshalers := [][]int(nil)544for i := 0; i != n; i++ {545field := st.Field(i)546if field.PkgPath != "" && !field.Anonymous {547continue // Private field548}549550info := fieldInfo{Num: i}551552tag := field.Tag.Get("yaml")553if tag == "" && strings.Index(string(field.Tag), ":") < 0 {554tag = string(field.Tag)555}556if tag == "-" {557continue558}559560inline := false561fields := strings.Split(tag, ",")562if len(fields) > 1 {563for _, flag := range fields[1:] {564switch flag {565case "omitempty":566info.OmitEmpty = true567case "flow":568info.Flow = true569case "inline":570inline = true571default:572return nil, errors.New(fmt.Sprintf("unsupported flag %q in tag %q of type %s", flag, tag, st))573}574}575tag = fields[0]576}577578if inline {579switch field.Type.Kind() {580case reflect.Map:581if inlineMap >= 0 {582return nil, errors.New("multiple ,inline maps in struct " + st.String())583}584if field.Type.Key() != reflect.TypeOf("") {585return nil, errors.New("option ,inline needs a map with string keys in struct " + st.String())586}587inlineMap = info.Num588case reflect.Struct, reflect.Ptr:589ftype := field.Type590for ftype.Kind() == reflect.Ptr {591ftype = ftype.Elem()592}593if ftype.Kind() != reflect.Struct {594return nil, errors.New("option ,inline may only be used on a struct or map field")595}596if reflect.PtrTo(ftype).Implements(unmarshalerType) {597inlineUnmarshalers = append(inlineUnmarshalers, []int{i})598} else {599sinfo, err := getStructInfo(ftype)600if err != nil {601return nil, err602}603for _, index := range sinfo.InlineUnmarshalers {604inlineUnmarshalers = append(inlineUnmarshalers, append([]int{i}, index...))605}606for _, finfo := range sinfo.FieldsList {607if _, found := fieldsMap[finfo.Key]; found {608msg := "duplicated key '" + finfo.Key + "' in struct " + st.String()609return nil, errors.New(msg)610}611if finfo.Inline == nil {612finfo.Inline = []int{i, finfo.Num}613} else {614finfo.Inline = append([]int{i}, finfo.Inline...)615}616finfo.Id = len(fieldsList)617fieldsMap[finfo.Key] = finfo618fieldsList = append(fieldsList, finfo)619}620}621default:622return nil, errors.New("option ,inline may only be used on a struct or map field")623}624continue625}626627if tag != "" {628info.Key = tag629} else {630info.Key = strings.ToLower(field.Name)631}632633if _, found = fieldsMap[info.Key]; found {634msg := "duplicated key '" + info.Key + "' in struct " + st.String()635return nil, errors.New(msg)636}637638info.Id = len(fieldsList)639fieldsList = append(fieldsList, info)640fieldsMap[info.Key] = info641}642643sinfo = &structInfo{644FieldsMap: fieldsMap,645FieldsList: fieldsList,646InlineMap: inlineMap,647InlineUnmarshalers: inlineUnmarshalers,648}649650fieldMapMutex.Lock()651structMap[st] = sinfo652fieldMapMutex.Unlock()653return sinfo, nil654}655656// IsZeroer is used to check whether an object is zero to657// determine whether it should be omitted when marshaling658// with the omitempty flag. One notable implementation659// is time.Time.660type IsZeroer interface {661IsZero() bool662}663664func isZero(v reflect.Value) bool {665kind := v.Kind()666if z, ok := v.Interface().(IsZeroer); ok {667if (kind == reflect.Ptr || kind == reflect.Interface) && v.IsNil() {668return true669}670return z.IsZero()671}672switch kind {673case reflect.String:674return len(v.String()) == 0675case reflect.Interface, reflect.Ptr:676return v.IsNil()677case reflect.Slice:678return v.Len() == 0679case reflect.Map:680return v.Len() == 0681case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:682return v.Int() == 0683case reflect.Float32, reflect.Float64:684return v.Float() == 0685case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:686return v.Uint() == 0687case reflect.Bool:688return !v.Bool()689case reflect.Struct:690vt := v.Type()691for i := v.NumField() - 1; i >= 0; i-- {692if vt.Field(i).PkgPath != "" {693continue // Private field694}695if !isZero(v.Field(i)) {696return false697}698}699return true700}701return false702}703704705