github.com/timstclair/heapster@v0.20.0-alpha1/Godeps/_workspace/src/gopkg.in/v2/yaml/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  
    36  // The Marshaler interface may be implemented by types to customize their
    37  // behavior when being marshaled into a YAML document. The returned value
    38  // is marshaled in place of the original value implementing Marshaler.
    39  //
    40  // If an error is returned by MarshalYAML, the marshaling procedure stops
    41  // and returns with the provided error.
    42  type Marshaler interface {
    43  	MarshalYAML() (interface{}, error)
    44  }
    45  
    46  // Unmarshal decodes the first document found within the in byte slice
    47  // and assigns decoded values into the out value.
    48  //
    49  // Maps and pointers (to a struct, string, int, etc) are accepted as out
    50  // values. If an internal pointer within a struct is not initialized,
    51  // the yaml package will initialize it if necessary for unmarshalling
    52  // the provided data. The out parameter must not be nil.
    53  //
    54  // The type of the decoded values should be compatible with the respective
    55  // values in out. If one or more values cannot be decoded due to a type
    56  // mismatches, decoding continues partially until the end of the YAML
    57  // content, and a *yaml.TypeError is returned with details for all
    58  // missed values.
    59  //
    60  // Struct fields are only unmarshalled if they are exported (have an
    61  // upper case first letter), and are unmarshalled using the field name
    62  // lowercased as the default key. Custom keys may be defined via the
    63  // "yaml" name in the field tag: the content preceding the first comma
    64  // is used as the key, and the following comma-separated options are
    65  // used to tweak the marshalling process (see Marshal).
    66  // Conflicting names result in a runtime error.
    67  //
    68  // For example:
    69  //
    70  //     type T struct {
    71  //         F int `yaml:"a,omitempty"`
    72  //         B int
    73  //     }
    74  //     var t T
    75  //     yaml.Unmarshal([]byte("a: 1\nb: 2"), &t)
    76  //
    77  // See the documentation of Marshal for the format of tags and a list of
    78  // supported tag options.
    79  //
    80  func Unmarshal(in []byte, out interface{}) (err error) {
    81  	defer handleErr(&err)
    82  	d := newDecoder()
    83  	p := newParser(in)
    84  	defer p.destroy()
    85  	node := p.parse()
    86  	if node != nil {
    87  		v := reflect.ValueOf(out)
    88  		if v.Kind() == reflect.Ptr && !v.IsNil() {
    89  			v = v.Elem()
    90  		}
    91  		d.unmarshal(node, v)
    92  	}
    93  	if d.terrors != nil {
    94  		return &TypeError{d.terrors}
    95  	}
    96  	return nil
    97  }
    98  
    99  // Marshal serializes the value provided into a YAML document. The structure
   100  // of the generated document will reflect the structure of the value itself.
   101  // Maps and pointers (to struct, string, int, etc) are accepted as the in value.
   102  //
   103  // Struct fields are only unmarshalled if they are exported (have an upper case
   104  // first letter), and are unmarshalled using the field name lowercased as the
   105  // default key. Custom keys may be defined via the "yaml" name in the field
   106  // tag: the content preceding the first comma is used as the key, and the
   107  // following comma-separated options are used to tweak the marshalling process.
   108  // Conflicting names result in a runtime error.
   109  //
   110  // The field tag format accepted is:
   111  //
   112  //     `(...) yaml:"[<key>][,<flag1>[,<flag2>]]" (...)`
   113  //
   114  // The following flags are currently supported:
   115  //
   116  //     omitempty    Only include the field if it's not set to the zero
   117  //                  value for the type or to empty slices or maps.
   118  //                  Does not apply to zero valued structs.
   119  //
   120  //     flow         Marshal using a flow style (useful for structs,
   121  //                  sequences and maps.
   122  //
   123  //     inline       Inline the struct it's applied to, so its fields
   124  //                  are processed as if they were part of the outer
   125  //                  struct.
   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 != "" {
   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  			// TODO: Implement support for inline maps.
   260  			//case reflect.Map:
   261  			//	if inlineMap >= 0 {
   262  			//		return nil, errors.New("Multiple ,inline maps in struct " + st.String())
   263  			//	}
   264  			//	if field.Type.Key() != reflect.TypeOf("") {
   265  			//		return nil, errors.New("Option ,inline needs a map with string keys in struct " + st.String())
   266  			//	}
   267  			//	inlineMap = info.Num
   268  			case reflect.Struct:
   269  				sinfo, err := getStructInfo(field.Type)
   270  				if err != nil {
   271  					return nil, err
   272  				}
   273  				for _, finfo := range sinfo.FieldsList {
   274  					if _, found := fieldsMap[finfo.Key]; found {
   275  						msg := "Duplicated key '" + finfo.Key + "' in struct " + st.String()
   276  						return nil, errors.New(msg)
   277  					}
   278  					if finfo.Inline == nil {
   279  						finfo.Inline = []int{i, finfo.Num}
   280  					} else {
   281  						finfo.Inline = append([]int{i}, finfo.Inline...)
   282  					}
   283  					fieldsMap[finfo.Key] = finfo
   284  					fieldsList = append(fieldsList, finfo)
   285  				}
   286  			default:
   287  				//return nil, errors.New("Option ,inline needs a struct value or map field")
   288  				return nil, errors.New("Option ,inline needs a struct value field")
   289  			}
   290  			continue
   291  		}
   292  
   293  		if tag != "" {
   294  			info.Key = tag
   295  		} else {
   296  			info.Key = strings.ToLower(field.Name)
   297  		}
   298  
   299  		if _, found = fieldsMap[info.Key]; found {
   300  			msg := "Duplicated key '" + info.Key + "' in struct " + st.String()
   301  			return nil, errors.New(msg)
   302  		}
   303  
   304  		fieldsList = append(fieldsList, info)
   305  		fieldsMap[info.Key] = info
   306  	}
   307  
   308  	sinfo = &structInfo{fieldsMap, fieldsList, inlineMap}
   309  
   310  	fieldMapMutex.Lock()
   311  	structMap[st] = sinfo
   312  	fieldMapMutex.Unlock()
   313  	return sinfo, nil
   314  }
   315  
   316  func isZero(v reflect.Value) bool {
   317  	switch v.Kind() {
   318  	case reflect.String:
   319  		return len(v.String()) == 0
   320  	case reflect.Interface, reflect.Ptr:
   321  		return v.IsNil()
   322  	case reflect.Slice:
   323  		return v.Len() == 0
   324  	case reflect.Map:
   325  		return v.Len() == 0
   326  	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
   327  		return v.Int() == 0
   328  	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
   329  		return v.Uint() == 0
   330  	case reflect.Bool:
   331  		return !v.Bool()
   332  	}
   333  	return false
   334  }