123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699 |
- //
- // Copyright (c) 2011-2019 Canonical Ltd
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- // Package yaml implements YAML support for the Go language.
- //
- // Source code and other details for the project are available at GitHub:
- //
- // https://github.com/go-yaml/yaml
- //
- package yaml
- import (
- "errors"
- "fmt"
- "io"
- "reflect"
- "strings"
- "sync"
- "unicode/utf8"
- )
- // The Unmarshaler interface may be implemented by types to customize their
- // behavior when being unmarshaled from a YAML document.
- type Unmarshaler interface {
- UnmarshalYAML(value *Node) error
- }
- type obsoleteUnmarshaler interface {
- UnmarshalYAML(unmarshal func(interface{}) error) error
- }
- // The Marshaler interface may be implemented by types to customize their
- // behavior when being marshaled into a YAML document. The returned value
- // is marshaled in place of the original value implementing Marshaler.
- //
- // If an error is returned by MarshalYAML, the marshaling procedure stops
- // and returns with the provided error.
- type Marshaler interface {
- MarshalYAML() (interface{}, error)
- }
- // Unmarshal decodes the first document found within the in byte slice
- // and assigns decoded values into the out value.
- //
- // Maps and pointers (to a struct, string, int, etc) are accepted as out
- // values. If an internal pointer within a struct is not initialized,
- // the yaml package will initialize it if necessary for unmarshalling
- // the provided data. The out parameter must not be nil.
- //
- // The type of the decoded values should be compatible with the respective
- // values in out. If one or more values cannot be decoded due to a type
- // mismatches, decoding continues partially until the end of the YAML
- // content, and a *yaml.TypeError is returned with details for all
- // missed values.
- //
- // Struct fields are only unmarshalled if they are exported (have an
- // upper case first letter), and are unmarshalled using the field name
- // lowercased as the default key. Custom keys may be defined via the
- // "yaml" name in the field tag: the content preceding the first comma
- // is used as the key, and the following comma-separated options are
- // used to tweak the marshalling process (see Marshal).
- // Conflicting names result in a runtime error.
- //
- // For example:
- //
- // type T struct {
- // F int `yaml:"a,omitempty"`
- // B int
- // }
- // var t T
- // yaml.Unmarshal([]byte("a: 1\nb: 2"), &t)
- //
- // See the documentation of Marshal for the format of tags and a list of
- // supported tag options.
- //
- func Unmarshal(in []byte, out interface{}) (err error) {
- return unmarshal(in, out, false)
- }
- // A Decoder reads and decodes YAML values from an input stream.
- type Decoder struct {
- parser *parser
- knownFields bool
- }
- // NewDecoder returns a new decoder that reads from r.
- //
- // The decoder introduces its own buffering and may read
- // data from r beyond the YAML values requested.
- func NewDecoder(r io.Reader) *Decoder {
- return &Decoder{
- parser: newParserFromReader(r),
- }
- }
- // KnownFields ensures that the keys in decoded mappings to
- // exist as fields in the struct being decoded into.
- func (dec *Decoder) KnownFields(enable bool) {
- dec.knownFields = enable
- }
- // Decode reads the next YAML-encoded value from its input
- // and stores it in the value pointed to by v.
- //
- // See the documentation for Unmarshal for details about the
- // conversion of YAML into a Go value.
- func (dec *Decoder) Decode(v interface{}) (err error) {
- d := newDecoder()
- d.knownFields = dec.knownFields
- defer handleErr(&err)
- node := dec.parser.parse()
- if node == nil {
- return io.EOF
- }
- out := reflect.ValueOf(v)
- if out.Kind() == reflect.Ptr && !out.IsNil() {
- out = out.Elem()
- }
- d.unmarshal(node, out)
- if len(d.terrors) > 0 {
- return &TypeError{d.terrors}
- }
- return nil
- }
- // Decode decodes the node and stores its data into the value pointed to by v.
- //
- // See the documentation for Unmarshal for details about the
- // conversion of YAML into a Go value.
- func (n *Node) Decode(v interface{}) (err error) {
- d := newDecoder()
- defer handleErr(&err)
- out := reflect.ValueOf(v)
- if out.Kind() == reflect.Ptr && !out.IsNil() {
- out = out.Elem()
- }
- d.unmarshal(n, out)
- if len(d.terrors) > 0 {
- return &TypeError{d.terrors}
- }
- return nil
- }
- func unmarshal(in []byte, out interface{}, strict bool) (err error) {
- defer handleErr(&err)
- d := newDecoder()
- p := newParser(in)
- defer p.destroy()
- node := p.parse()
- if node != nil {
- v := reflect.ValueOf(out)
- if v.Kind() == reflect.Ptr && !v.IsNil() {
- v = v.Elem()
- }
- d.unmarshal(node, v)
- }
- if len(d.terrors) > 0 {
- return &TypeError{d.terrors}
- }
- return nil
- }
- // Marshal serializes the value provided into a YAML document. The structure
- // of the generated document will reflect the structure of the value itself.
- // Maps and pointers (to struct, string, int, etc) are accepted as the in value.
- //
- // Struct fields are only marshalled if they are exported (have an upper case
- // first letter), and are marshalled using the field name lowercased as the
- // default key. Custom keys may be defined via the "yaml" name in the field
- // tag: the content preceding the first comma is used as the key, and the
- // following comma-separated options are used to tweak the marshalling process.
- // Conflicting names result in a runtime error.
- //
- // The field tag format accepted is:
- //
- // `(...) yaml:"[<key>][,<flag1>[,<flag2>]]" (...)`
- //
- // The following flags are currently supported:
- //
- // omitempty Only include the field if it's not set to the zero
- // value for the type or to empty slices or maps.
- // Zero valued structs will be omitted if all their public
- // fields are zero, unless they implement an IsZero
- // method (see the IsZeroer interface type), in which
- // case the field will be excluded if IsZero returns true.
- //
- // flow Marshal using a flow style (useful for structs,
- // sequences and maps).
- //
- // inline Inline the field, which must be a struct or a map,
- // causing all of its fields or keys to be processed as if
- // they were part of the outer struct. For maps, keys must
- // not conflict with the yaml keys of other struct fields.
- //
- // In addition, if the key is "-", the field is ignored.
- //
- // For example:
- //
- // type T struct {
- // F int `yaml:"a,omitempty"`
- // B int
- // }
- // yaml.Marshal(&T{B: 2}) // Returns "b: 2\n"
- // yaml.Marshal(&T{F: 1}} // Returns "a: 1\nb: 0\n"
- //
- func Marshal(in interface{}) (out []byte, err error) {
- defer handleErr(&err)
- e := newEncoder()
- defer e.destroy()
- e.marshalDoc("", reflect.ValueOf(in))
- e.finish()
- out = e.out
- return
- }
- // An Encoder writes YAML values to an output stream.
- type Encoder struct {
- encoder *encoder
- }
- // NewEncoder returns a new encoder that writes to w.
- // The Encoder should be closed after use to flush all data
- // to w.
- func NewEncoder(w io.Writer) *Encoder {
- return &Encoder{
- encoder: newEncoderWithWriter(w),
- }
- }
- // Encode writes the YAML encoding of v to the stream.
- // If multiple items are encoded to the stream, the
- // second and subsequent document will be preceded
- // with a "---" document separator, but the first will not.
- //
- // See the documentation for Marshal for details about the conversion of Go
- // values to YAML.
- func (e *Encoder) Encode(v interface{}) (err error) {
- defer handleErr(&err)
- e.encoder.marshalDoc("", reflect.ValueOf(v))
- return nil
- }
- // Encode encodes value v and stores its representation in n.
- //
- // See the documentation for Marshal for details about the
- // conversion of Go values into YAML.
- func (n *Node) Encode(v interface{}) (err error) {
- defer handleErr(&err)
- e := newEncoder()
- defer e.destroy()
- e.marshalDoc("", reflect.ValueOf(v))
- e.finish()
- p := newParser(e.out)
- p.textless = true
- defer p.destroy()
- doc := p.parse()
- *n = *doc.Content[0]
- return nil
- }
- // SetIndent changes the used indentation used when encoding.
- func (e *Encoder) SetIndent(spaces int) {
- if spaces < 0 {
- panic("yaml: cannot indent to a negative number of spaces")
- }
- e.encoder.indent = spaces
- }
- // Close closes the encoder by writing any remaining data.
- // It does not write a stream terminating string "...".
- func (e *Encoder) Close() (err error) {
- defer handleErr(&err)
- e.encoder.finish()
- return nil
- }
- func handleErr(err *error) {
- if v := recover(); v != nil {
- if e, ok := v.(yamlError); ok {
- *err = e.err
- } else {
- panic(v)
- }
- }
- }
- type yamlError struct {
- err error
- }
- func fail(err error) {
- panic(yamlError{err})
- }
- func failf(format string, args ...interface{}) {
- panic(yamlError{fmt.Errorf("yaml: "+format, args...)})
- }
- // A TypeError is returned by Unmarshal when one or more fields in
- // the YAML document cannot be properly decoded into the requested
- // types. When this error is returned, the value is still
- // unmarshaled partially.
- type TypeError struct {
- Errors []string
- }
- func (e *TypeError) Error() string {
- return fmt.Sprintf("yaml: unmarshal errors:\n %s", strings.Join(e.Errors, "\n "))
- }
- type Kind uint32
- const (
- DocumentNode Kind = 1 << iota
- SequenceNode
- MappingNode
- ScalarNode
- AliasNode
- )
- type Style uint32
- const (
- TaggedStyle Style = 1 << iota
- DoubleQuotedStyle
- SingleQuotedStyle
- LiteralStyle
- FoldedStyle
- FlowStyle
- )
- // Node represents an element in the YAML document hierarchy. While documents
- // are typically encoded and decoded into higher level types, such as structs
- // and maps, Node is an intermediate representation that allows detailed
- // control over the content being decoded or encoded.
- //
- // It's worth noting that although Node offers access into details such as
- // line numbers, colums, and comments, the content when re-encoded will not
- // have its original textual representation preserved. An effort is made to
- // render the data plesantly, and to preserve comments near the data they
- // describe, though.
- //
- // Values that make use of the Node type interact with the yaml package in the
- // same way any other type would do, by encoding and decoding yaml data
- // directly or indirectly into them.
- //
- // For example:
- //
- // var person struct {
- // Name string
- // Address yaml.Node
- // }
- // err := yaml.Unmarshal(data, &person)
- //
- // Or by itself:
- //
- // var person Node
- // err := yaml.Unmarshal(data, &person)
- //
- type Node struct {
- // Kind defines whether the node is a document, a mapping, a sequence,
- // a scalar value, or an alias to another node. The specific data type of
- // scalar nodes may be obtained via the ShortTag and LongTag methods.
- Kind Kind
- // Style allows customizing the apperance of the node in the tree.
- Style Style
- // Tag holds the YAML tag defining the data type for the value.
- // When decoding, this field will always be set to the resolved tag,
- // even when it wasn't explicitly provided in the YAML content.
- // When encoding, if this field is unset the value type will be
- // implied from the node properties, and if it is set, it will only
- // be serialized into the representation if TaggedStyle is used or
- // the implicit tag diverges from the provided one.
- Tag string
- // Value holds the unescaped and unquoted represenation of the value.
- Value string
- // Anchor holds the anchor name for this node, which allows aliases to point to it.
- Anchor string
- // Alias holds the node that this alias points to. Only valid when Kind is AliasNode.
- Alias *Node
- // Content holds contained nodes for documents, mappings, and sequences.
- Content []*Node
- // HeadComment holds any comments in the lines preceding the node and
- // not separated by an empty line.
- HeadComment string
- // LineComment holds any comments at the end of the line where the node is in.
- LineComment string
- // FootComment holds any comments following the node and before empty lines.
- FootComment string
- // Line and Column hold the node position in the decoded YAML text.
- // These fields are not respected when encoding the node.
- Line int
- Column int
- }
- // IsZero returns whether the node has all of its fields unset.
- func (n *Node) IsZero() bool {
- return n.Kind == 0 && n.Style == 0 && n.Tag == "" && n.Value == "" && n.Anchor == "" && n.Alias == nil && n.Content == nil &&
- n.HeadComment == "" && n.LineComment == "" && n.FootComment == "" && n.Line == 0 && n.Column == 0
- }
- // LongTag returns the long form of the tag that indicates the data type for
- // the node. If the Tag field isn't explicitly defined, one will be computed
- // based on the node properties.
- func (n *Node) LongTag() string {
- return longTag(n.ShortTag())
- }
- // ShortTag returns the short form of the YAML tag that indicates data type for
- // the node. If the Tag field isn't explicitly defined, one will be computed
- // based on the node properties.
- func (n *Node) ShortTag() string {
- if n.indicatedString() {
- return strTag
- }
- if n.Tag == "" || n.Tag == "!" {
- switch n.Kind {
- case MappingNode:
- return mapTag
- case SequenceNode:
- return seqTag
- case AliasNode:
- if n.Alias != nil {
- return n.Alias.ShortTag()
- }
- case ScalarNode:
- tag, _ := resolve("", n.Value)
- return tag
- case 0:
- // Special case to make the zero value convenient.
- if n.IsZero() {
- return nullTag
- }
- }
- return ""
- }
- return shortTag(n.Tag)
- }
- func (n *Node) indicatedString() bool {
- return n.Kind == ScalarNode &&
- (shortTag(n.Tag) == strTag ||
- (n.Tag == "" || n.Tag == "!") && n.Style&(SingleQuotedStyle|DoubleQuotedStyle|LiteralStyle|FoldedStyle) != 0)
- }
- // SetString is a convenience function that sets the node to a string value
- // and defines its style in a pleasant way depending on its content.
- func (n *Node) SetString(s string) {
- n.Kind = ScalarNode
- if utf8.ValidString(s) {
- n.Value = s
- n.Tag = strTag
- } else {
- n.Value = encodeBase64(s)
- n.Tag = binaryTag
- }
- if strings.Contains(n.Value, "\n") {
- n.Style = LiteralStyle
- }
- }
- // --------------------------------------------------------------------------
- // Maintain a mapping of keys to structure field indexes
- // The code in this section was copied from mgo/bson.
- // structInfo holds details for the serialization of fields of
- // a given struct.
- type structInfo struct {
- FieldsMap map[string]fieldInfo
- FieldsList []fieldInfo
- // InlineMap is the number of the field in the struct that
- // contains an ,inline map, or -1 if there's none.
- InlineMap int
- // InlineUnmarshalers holds indexes to inlined fields that
- // contain unmarshaler values.
- InlineUnmarshalers [][]int
- }
- type fieldInfo struct {
- Key string
- Num int
- OmitEmpty bool
- Flow bool
- // Id holds the unique field identifier, so we can cheaply
- // check for field duplicates without maintaining an extra map.
- Id int
- // Inline holds the field index if the field is part of an inlined struct.
- Inline []int
- }
- var structMap = make(map[reflect.Type]*structInfo)
- var fieldMapMutex sync.RWMutex
- var unmarshalerType reflect.Type
- func init() {
- var v Unmarshaler
- unmarshalerType = reflect.ValueOf(&v).Elem().Type()
- }
- func getStructInfo(st reflect.Type) (*structInfo, error) {
- fieldMapMutex.RLock()
- sinfo, found := structMap[st]
- fieldMapMutex.RUnlock()
- if found {
- return sinfo, nil
- }
- n := st.NumField()
- fieldsMap := make(map[string]fieldInfo)
- fieldsList := make([]fieldInfo, 0, n)
- inlineMap := -1
- inlineUnmarshalers := [][]int(nil)
- for i := 0; i != n; i++ {
- field := st.Field(i)
- if field.PkgPath != "" && !field.Anonymous {
- continue // Private field
- }
- info := fieldInfo{Num: i}
- tag := field.Tag.Get("yaml")
- if tag == "" && strings.Index(string(field.Tag), ":") < 0 {
- tag = string(field.Tag)
- }
- if tag == "-" {
- continue
- }
- inline := false
- fields := strings.Split(tag, ",")
- if len(fields) > 1 {
- for _, flag := range fields[1:] {
- switch flag {
- case "omitempty":
- info.OmitEmpty = true
- case "flow":
- info.Flow = true
- case "inline":
- inline = true
- default:
- return nil, errors.New(fmt.Sprintf("unsupported flag %q in tag %q of type %s", flag, tag, st))
- }
- }
- tag = fields[0]
- }
- if inline {
- switch field.Type.Kind() {
- case reflect.Map:
- if inlineMap >= 0 {
- return nil, errors.New("multiple ,inline maps in struct " + st.String())
- }
- if field.Type.Key() != reflect.TypeOf("") {
- return nil, errors.New("option ,inline needs a map with string keys in struct " + st.String())
- }
- inlineMap = info.Num
- case reflect.Struct, reflect.Ptr:
- ftype := field.Type
- for ftype.Kind() == reflect.Ptr {
- ftype = ftype.Elem()
- }
- if ftype.Kind() != reflect.Struct {
- return nil, errors.New("option ,inline may only be used on a struct or map field")
- }
- if reflect.PtrTo(ftype).Implements(unmarshalerType) {
- inlineUnmarshalers = append(inlineUnmarshalers, []int{i})
- } else {
- sinfo, err := getStructInfo(ftype)
- if err != nil {
- return nil, err
- }
- for _, index := range sinfo.InlineUnmarshalers {
- inlineUnmarshalers = append(inlineUnmarshalers, append([]int{i}, index...))
- }
- for _, finfo := range sinfo.FieldsList {
- if _, found := fieldsMap[finfo.Key]; found {
- msg := "duplicated key '" + finfo.Key + "' in struct " + st.String()
- return nil, errors.New(msg)
- }
- if finfo.Inline == nil {
- finfo.Inline = []int{i, finfo.Num}
- } else {
- finfo.Inline = append([]int{i}, finfo.Inline...)
- }
- finfo.Id = len(fieldsList)
- fieldsMap[finfo.Key] = finfo
- fieldsList = append(fieldsList, finfo)
- }
- }
- default:
- return nil, errors.New("option ,inline may only be used on a struct or map field")
- }
- continue
- }
- if tag != "" {
- info.Key = tag
- } else {
- info.Key = strings.ToLower(field.Name)
- }
- if _, found = fieldsMap[info.Key]; found {
- msg := "duplicated key '" + info.Key + "' in struct " + st.String()
- return nil, errors.New(msg)
- }
- info.Id = len(fieldsList)
- fieldsList = append(fieldsList, info)
- fieldsMap[info.Key] = info
- }
- sinfo = &structInfo{
- FieldsMap: fieldsMap,
- FieldsList: fieldsList,
- InlineMap: inlineMap,
- InlineUnmarshalers: inlineUnmarshalers,
- }
- fieldMapMutex.Lock()
- structMap[st] = sinfo
- fieldMapMutex.Unlock()
- return sinfo, nil
- }
- // IsZeroer is used to check whether an object is zero to
- // determine whether it should be omitted when marshaling
- // with the omitempty flag. One notable implementation
- // is time.Time.
- type IsZeroer interface {
- IsZero() bool
- }
- func isZero(v reflect.Value) bool {
- kind := v.Kind()
- if z, ok := v.Interface().(IsZeroer); ok {
- if (kind == reflect.Ptr || kind == reflect.Interface) && v.IsNil() {
- return true
- }
- return z.IsZero()
- }
- switch kind {
- case reflect.String:
- return len(v.String()) == 0
- case reflect.Interface, reflect.Ptr:
- return v.IsNil()
- case reflect.Slice:
- return v.Len() == 0
- case reflect.Map:
- return v.Len() == 0
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- return v.Int() == 0
- case reflect.Float32, reflect.Float64:
- return v.Float() == 0
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
- return v.Uint() == 0
- case reflect.Bool:
- return !v.Bool()
- case reflect.Struct:
- vt := v.Type()
- for i := v.NumField() - 1; i >= 0; i-- {
- if vt.Field(i).PkgPath != "" {
- continue // Private field
- }
- if !isZero(v.Field(i)) {
- return false
- }
- }
- return true
- }
- return false
- }
|