github.com/trevoraustin/hub@v2.2.0-preview1.0.20141105230840-96d8bfc654cc+incompatible/Godeps/_workspace/src/gopkg.in/yaml.v1/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  	"runtime"
    14  	"strings"
    15  	"sync"
    16  )
    17  
    18  func handleErr(err *error) {
    19  	if r := recover(); r != nil {
    20  		if _, ok := r.(runtime.Error); ok {
    21  			panic(r)
    22  		} else if _, ok := r.(*reflect.ValueError); ok {
    23  			panic(r)
    24  		} else if _, ok := r.(externalPanic); ok {
    25  			panic(r)
    26  		} else if s, ok := r.(string); ok {
    27  			*err = errors.New("YAML error: " + s)
    28  		} else if e, ok := r.(error); ok {
    29  			*err = e
    30  		} else {
    31  			panic(r)
    32  		}
    33  	}
    34  }
    35  
    36  // The Setter interface may be implemented by types to do their own custom
    37  // unmarshalling of YAML values, rather than being implicitly assigned by
    38  // the yaml package machinery. If setting the value works, the method should
    39  // return true.  If it returns false, the value is considered unsupported
    40  // and is omitted from maps and slices.
    41  type Setter interface {
    42  	SetYAML(tag string, value interface{}) bool
    43  }
    44  
    45  // The Getter interface is implemented by types to do their own custom
    46  // marshalling into a YAML tag and value.
    47  type Getter interface {
    48  	GetYAML() (tag string, value interface{})
    49  }
    50  
    51  // Unmarshal decodes the first document found within the in byte slice
    52  // and assigns decoded values into the out value.
    53  //
    54  // Maps and pointers (to a struct, string, int, etc) are accepted as out
    55  // values.  If an internal pointer within a struct is not initialized,
    56  // the yaml package will initialize it if necessary for unmarshalling
    57  // the provided data. The out parameter must not be nil.
    58  //
    59  // The type of the decoded values and the type of out will be considered,
    60  // and Unmarshal will do the best possible job to unmarshal values
    61  // appropriately.  It is NOT considered an error, though, to skip values
    62  // because they are not available in the decoded YAML, or if they are not
    63  // compatible with the out value. To ensure something was properly
    64  // unmarshaled use a map or compare against the previous value for the
    65  // field (usually the zero value).
    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.
    86  //
    87  func Unmarshal(in []byte, out interface{}) (err error) {
    88  	defer handleErr(&err)
    89  	d := newDecoder()
    90  	p := newParser(in)
    91  	defer p.destroy()
    92  	node := p.parse()
    93  	if node != nil {
    94  		d.unmarshal(node, reflect.ValueOf(out))
    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  // --------------------------------------------------------------------------
   149  // Maintain a mapping of keys to structure field indexes
   150  
   151  // The code in this section was copied from mgo/bson.
   152  
   153  // structInfo holds details for the serialization of fields of
   154  // a given struct.
   155  type structInfo struct {
   156  	FieldsMap  map[string]fieldInfo
   157  	FieldsList []fieldInfo
   158  
   159  	// InlineMap is the number of the field in the struct that
   160  	// contains an ,inline map, or -1 if there's none.
   161  	InlineMap int
   162  }
   163  
   164  type fieldInfo struct {
   165  	Key       string
   166  	Num       int
   167  	OmitEmpty bool
   168  	Flow      bool
   169  
   170  	// Inline holds the field index if the field is part of an inlined struct.
   171  	Inline []int
   172  }
   173  
   174  var structMap = make(map[reflect.Type]*structInfo)
   175  var fieldMapMutex sync.RWMutex
   176  
   177  type externalPanic string
   178  
   179  func (e externalPanic) String() string {
   180  	return string(e)
   181  }
   182  
   183  func getStructInfo(st reflect.Type) (*structInfo, error) {
   184  	fieldMapMutex.RLock()
   185  	sinfo, found := structMap[st]
   186  	fieldMapMutex.RUnlock()
   187  	if found {
   188  		return sinfo, nil
   189  	}
   190  
   191  	n := st.NumField()
   192  	fieldsMap := make(map[string]fieldInfo)
   193  	fieldsList := make([]fieldInfo, 0, n)
   194  	inlineMap := -1
   195  	for i := 0; i != n; i++ {
   196  		field := st.Field(i)
   197  		if field.PkgPath != "" {
   198  			continue // Private field
   199  		}
   200  
   201  		info := fieldInfo{Num: i}
   202  
   203  		tag := field.Tag.Get("yaml")
   204  		if tag == "" && strings.Index(string(field.Tag), ":") < 0 {
   205  			tag = string(field.Tag)
   206  		}
   207  		if tag == "-" {
   208  			continue
   209  		}
   210  
   211  		inline := false
   212  		fields := strings.Split(tag, ",")
   213  		if len(fields) > 1 {
   214  			for _, flag := range fields[1:] {
   215  				switch flag {
   216  				case "omitempty":
   217  					info.OmitEmpty = true
   218  				case "flow":
   219  					info.Flow = true
   220  				case "inline":
   221  					inline = true
   222  				default:
   223  					msg := fmt.Sprintf("Unsupported flag %q in tag %q of type %s", flag, tag, st)
   224  					panic(externalPanic(msg))
   225  				}
   226  			}
   227  			tag = fields[0]
   228  		}
   229  
   230  		if inline {
   231  			switch field.Type.Kind() {
   232  			//case reflect.Map:
   233  			//	if inlineMap >= 0 {
   234  			//		return nil, errors.New("Multiple ,inline maps in struct " + st.String())
   235  			//	}
   236  			//	if field.Type.Key() != reflect.TypeOf("") {
   237  			//		return nil, errors.New("Option ,inline needs a map with string keys in struct " + st.String())
   238  			//	}
   239  			//	inlineMap = info.Num
   240  			case reflect.Struct:
   241  				sinfo, err := getStructInfo(field.Type)
   242  				if err != nil {
   243  					return nil, err
   244  				}
   245  				for _, finfo := range sinfo.FieldsList {
   246  					if _, found := fieldsMap[finfo.Key]; found {
   247  						msg := "Duplicated key '" + finfo.Key + "' in struct " + st.String()
   248  						return nil, errors.New(msg)
   249  					}
   250  					if finfo.Inline == nil {
   251  						finfo.Inline = []int{i, finfo.Num}
   252  					} else {
   253  						finfo.Inline = append([]int{i}, finfo.Inline...)
   254  					}
   255  					fieldsMap[finfo.Key] = finfo
   256  					fieldsList = append(fieldsList, finfo)
   257  				}
   258  			default:
   259  				//panic("Option ,inline needs a struct value or map field")
   260  				panic("Option ,inline needs a struct value field")
   261  			}
   262  			continue
   263  		}
   264  
   265  		if tag != "" {
   266  			info.Key = tag
   267  		} else {
   268  			info.Key = strings.ToLower(field.Name)
   269  		}
   270  
   271  		if _, found = fieldsMap[info.Key]; found {
   272  			msg := "Duplicated key '" + info.Key + "' in struct " + st.String()
   273  			return nil, errors.New(msg)
   274  		}
   275  
   276  		fieldsList = append(fieldsList, info)
   277  		fieldsMap[info.Key] = info
   278  	}
   279  
   280  	sinfo = &structInfo{fieldsMap, fieldsList, inlineMap}
   281  
   282  	fieldMapMutex.Lock()
   283  	structMap[st] = sinfo
   284  	fieldMapMutex.Unlock()
   285  	return sinfo, nil
   286  }
   287  
   288  func isZero(v reflect.Value) bool {
   289  	switch v.Kind() {
   290  	case reflect.String:
   291  		return len(v.String()) == 0
   292  	case reflect.Interface, reflect.Ptr:
   293  		return v.IsNil()
   294  	case reflect.Slice:
   295  		return v.Len() == 0
   296  	case reflect.Map:
   297  		return v.Len() == 0
   298  	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
   299  		return v.Int() == 0
   300  	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
   301  		return v.Uint() == 0
   302  	case reflect.Bool:
   303  		return !v.Bool()
   304  	}
   305  	return false
   306  }