github.com/mfpierre/corectl@v0.5.6/Godeps/_workspace/src/gopkg.in/yaml.v2/yaml.go (about)

     1  // Package yaml implements YAML support for the Go language.
     2  //
     3  // Source code and other details for the project are available at GitHub:
     4  //
     5  //   https://github.com/go-yaml/yaml
     6  //
     7  package yaml
     8  
     9  import (
    10  	"errors"
    11  	"fmt"
    12  	"reflect"
    13  	"strings"
    14  	"sync"
    15  )
    16  
    17  // MapSlice encodes and decodes as a YAML map.
    18  // The order of keys is preserved when encoding and decoding.
    19  type MapSlice []MapItem
    20  
    21  // MapItem is an item in a MapSlice.
    22  type MapItem struct {
    23  	Key, Value interface{}
    24  }
    25  
    26  // The Unmarshaler interface may be implemented by types to customize their
    27  // behavior when being unmarshaled from a YAML document. The UnmarshalYAML
    28  // method receives a function that may be called to unmarshal the original
    29  // YAML value into a field or variable. It is safe to call the unmarshal
    30  // function parameter more than once if necessary.
    31  type Unmarshaler interface {
    32  	UnmarshalYAML(unmarshal func(interface{}) error) error
    33  }
    34  
    35  // The Marshaler interface may be implemented by types to customize their
    36  // behavior when being marshaled into a YAML document. The returned value
    37  // is marshaled in place of the original value implementing Marshaler.
    38  //
    39  // If an error is returned by MarshalYAML, the marshaling procedure stops
    40  // and returns with the provided error.
    41  type Marshaler interface {
    42  	MarshalYAML() (interface{}, error)
    43  }
    44  
    45  // Unmarshal decodes the first document found within the in byte slice
    46  // and assigns decoded values into the out value.
    47  //
    48  // Maps and pointers (to a struct, string, int, etc) are accepted as out
    49  // values. If an internal pointer within a struct is not initialized,
    50  // the yaml package will initialize it if necessary for unmarshalling
    51  // the provided data. The out parameter must not be nil.
    52  //
    53  // The type of the decoded values should be compatible with the respective
    54  // values in out. If one or more values cannot be decoded due to a type
    55  // mismatches, decoding continues partially until the end of the YAML
    56  // content, and a *yaml.TypeError is returned with details for all
    57  // missed values.
    58  //
    59  // Struct fields are only unmarshalled if they are exported (have an
    60  // upper case first letter), and are unmarshalled using the field name
    61  // lowercased as the default key. Custom keys may be defined via the
    62  // "yaml" name in the field tag: the content preceding the first comma
    63  // is used as the key, and the following comma-separated options are
    64  // used to tweak the marshalling process (see Marshal).
    65  // Conflicting names result in a runtime error.
    66  //
    67  // For example:
    68  //
    69  //     type T struct {
    70  //         F int `yaml:"a,omitempty"`
    71  //         B int
    72  //     }
    73  //     var t T
    74  //     yaml.Unmarshal([]byte("a: 1\nb: 2"), &t)
    75  //
    76  // See the documentation of Marshal for the format of tags and a list of
    77  // supported tag options.
    78  //
    79  func Unmarshal(in []byte, out interface{}) (err error) {
    80  	defer handleErr(&err)
    81  	d := newDecoder()
    82  	p := newParser(in)
    83  	defer p.destroy()
    84  	node := p.parse()
    85  	if node != nil {
    86  		v := reflect.ValueOf(out)
    87  		if v.Kind() == reflect.Ptr && !v.IsNil() {
    88  			v = v.Elem()
    89  		}
    90  		d.unmarshal(node, v)
    91  	}
    92  	if len(d.terrors) > 0 {
    93  		return &TypeError{d.terrors}
    94  	}
    95  	return nil
    96  }
    97  
    98  // Marshal serializes the value provided into a YAML document. The structure
    99  // of the generated document will reflect the structure of the value itself.
   100  // Maps and pointers (to struct, string, int, etc) are accepted as the in value.
   101  //
   102  // Struct fields are only unmarshalled if they are exported (have an upper case
   103  // first letter), and are unmarshalled using the field name lowercased as the
   104  // default key. Custom keys may be defined via the "yaml" name in the field
   105  // tag: the content preceding the first comma is used as the key, and the
   106  // following comma-separated options are used to tweak the marshalling process.
   107  // Conflicting names result in a runtime error.
   108  //
   109  // The field tag format accepted is:
   110  //
   111  //     `(...) yaml:"[<key>][,<flag1>[,<flag2>]]" (...)`
   112  //
   113  // The following flags are currently supported:
   114  //
   115  //     omitempty    Only include the field if it's not set to the zero
   116  //                  value for the type or to empty slices or maps.
   117  //                  Does not apply to zero valued structs.
   118  //
   119  //     flow         Marshal using a flow style (useful for structs,
   120  //                  sequences and maps).
   121  //
   122  //     inline       Inline the field, which must be a struct or a map,
   123  //                  causing all of its fields or keys to be processed as if
   124  //                  they were part of the outer struct. For maps, keys must
   125  //                  not conflict with the yaml keys of other struct fields.
   126  //
   127  // In addition, if the key is "-", the field is ignored.
   128  //
   129  // For example:
   130  //
   131  //     type T struct {
   132  //         F int "a,omitempty"
   133  //         B int
   134  //     }
   135  //     yaml.Marshal(&T{B: 2}) // Returns "b: 2\n"
   136  //     yaml.Marshal(&T{F: 1}} // Returns "a: 1\nb: 0\n"
   137  //
   138  func Marshal(in interface{}) (out []byte, err error) {
   139  	defer handleErr(&err)
   140  	e := newEncoder()
   141  	defer e.destroy()
   142  	e.marshal("", reflect.ValueOf(in))
   143  	e.finish()
   144  	out = e.out
   145  	return
   146  }
   147  
   148  func handleErr(err *error) {
   149  	if v := recover(); v != nil {
   150  		if e, ok := v.(yamlError); ok {
   151  			*err = e.err
   152  		} else {
   153  			panic(v)
   154  		}
   155  	}
   156  }
   157  
   158  type yamlError struct {
   159  	err error
   160  }
   161  
   162  func fail(err error) {
   163  	panic(yamlError{err})
   164  }
   165  
   166  func failf(format string, args ...interface{}) {
   167  	panic(yamlError{fmt.Errorf("yaml: "+format, args...)})
   168  }
   169  
   170  // A TypeError is returned by Unmarshal when one or more fields in
   171  // the YAML document cannot be properly decoded into the requested
   172  // types. When this error is returned, the value is still
   173  // unmarshaled partially.
   174  type TypeError struct {
   175  	Errors []string
   176  }
   177  
   178  func (e *TypeError) Error() string {
   179  	return fmt.Sprintf("yaml: unmarshal errors:\n  %s", strings.Join(e.Errors, "\n  "))
   180  }
   181  
   182  // --------------------------------------------------------------------------
   183  // Maintain a mapping of keys to structure field indexes
   184  
   185  // The code in this section was copied from mgo/bson.
   186  
   187  // structInfo holds details for the serialization of fields of
   188  // a given struct.
   189  type structInfo struct {
   190  	FieldsMap  map[string]fieldInfo
   191  	FieldsList []fieldInfo
   192  
   193  	// InlineMap is the number of the field in the struct that
   194  	// contains an ,inline map, or -1 if there's none.
   195  	InlineMap int
   196  }
   197  
   198  type fieldInfo struct {
   199  	Key       string
   200  	Num       int
   201  	OmitEmpty bool
   202  	Flow      bool
   203  
   204  	// Inline holds the field index if the field is part of an inlined struct.
   205  	Inline []int
   206  }
   207  
   208  var structMap = make(map[reflect.Type]*structInfo)
   209  var fieldMapMutex sync.RWMutex
   210  
   211  func getStructInfo(st reflect.Type) (*structInfo, error) {
   212  	fieldMapMutex.RLock()
   213  	sinfo, found := structMap[st]
   214  	fieldMapMutex.RUnlock()
   215  	if found {
   216  		return sinfo, nil
   217  	}
   218  
   219  	n := st.NumField()
   220  	fieldsMap := make(map[string]fieldInfo)
   221  	fieldsList := make([]fieldInfo, 0, n)
   222  	inlineMap := -1
   223  	for i := 0; i != n; i++ {
   224  		field := st.Field(i)
   225  		if field.PkgPath != "" && !field.Anonymous {
   226  			continue // Private field
   227  		}
   228  
   229  		info := fieldInfo{Num: i}
   230  
   231  		tag := field.Tag.Get("yaml")
   232  		if tag == "" && strings.Index(string(field.Tag), ":") < 0 {
   233  			tag = string(field.Tag)
   234  		}
   235  		if tag == "-" {
   236  			continue
   237  		}
   238  
   239  		inline := false
   240  		fields := strings.Split(tag, ",")
   241  		if len(fields) > 1 {
   242  			for _, flag := range fields[1:] {
   243  				switch flag {
   244  				case "omitempty":
   245  					info.OmitEmpty = true
   246  				case "flow":
   247  					info.Flow = true
   248  				case "inline":
   249  					inline = true
   250  				default:
   251  					return nil, errors.New(fmt.Sprintf("Unsupported flag %q in tag %q of type %s", flag, tag, st))
   252  				}
   253  			}
   254  			tag = fields[0]
   255  		}
   256  
   257  		if inline {
   258  			switch field.Type.Kind() {
   259  			case reflect.Map:
   260  				if inlineMap >= 0 {
   261  					return nil, errors.New("Multiple ,inline maps in struct " + st.String())
   262  				}
   263  				if field.Type.Key() != reflect.TypeOf("") {
   264  					return nil, errors.New("Option ,inline needs a map with string keys in struct " + st.String())
   265  				}
   266  				inlineMap = info.Num
   267  			case reflect.Struct:
   268  				sinfo, err := getStructInfo(field.Type)
   269  				if err != nil {
   270  					return nil, err
   271  				}
   272  				for _, finfo := range sinfo.FieldsList {
   273  					if _, found := fieldsMap[finfo.Key]; found {
   274  						msg := "Duplicated key '" + finfo.Key + "' in struct " + st.String()
   275  						return nil, errors.New(msg)
   276  					}
   277  					if finfo.Inline == nil {
   278  						finfo.Inline = []int{i, finfo.Num}
   279  					} else {
   280  						finfo.Inline = append([]int{i}, finfo.Inline...)
   281  					}
   282  					fieldsMap[finfo.Key] = finfo
   283  					fieldsList = append(fieldsList, finfo)
   284  				}
   285  			default:
   286  				//return nil, errors.New("Option ,inline needs a struct value or map field")
   287  				return nil, errors.New("Option ,inline needs a struct value field")
   288  			}
   289  			continue
   290  		}
   291  
   292  		if tag != "" {
   293  			info.Key = tag
   294  		} else {
   295  			info.Key = strings.ToLower(field.Name)
   296  		}
   297  
   298  		if _, found = fieldsMap[info.Key]; found {
   299  			msg := "Duplicated key '" + info.Key + "' in struct " + st.String()
   300  			return nil, errors.New(msg)
   301  		}
   302  
   303  		fieldsList = append(fieldsList, info)
   304  		fieldsMap[info.Key] = info
   305  	}
   306  
   307  	sinfo = &structInfo{fieldsMap, fieldsList, inlineMap}
   308  
   309  	fieldMapMutex.Lock()
   310  	structMap[st] = sinfo
   311  	fieldMapMutex.Unlock()
   312  	return sinfo, nil
   313  }
   314  
   315  func isZero(v reflect.Value) bool {
   316  	switch v.Kind() {
   317  	case reflect.String:
   318  		return len(v.String()) == 0
   319  	case reflect.Interface, reflect.Ptr:
   320  		return v.IsNil()
   321  	case reflect.Slice:
   322  		return v.Len() == 0
   323  	case reflect.Map:
   324  		return v.Len() == 0
   325  	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
   326  		return v.Int() == 0
   327  	case reflect.Float32, reflect.Float64:
   328  		return v.Float() == 0
   329  	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
   330  		return v.Uint() == 0
   331  	case reflect.Bool:
   332  		return !v.Bool()
   333  	case reflect.Struct:
   334  		vt := v.Type()
   335  		for i := v.NumField() - 1; i >= 0; i-- {
   336  			if vt.Field(i).PkgPath != "" {
   337  				continue // Private field
   338  			}
   339  			if !isZero(v.Field(i)) {
   340  				return false
   341  			}
   342  		}
   343  		return true
   344  	}
   345  	return false
   346  }