github.com/marinho/drone@v0.2.1-0.20140504195434-d3ba962e89a7/Godeps/_workspace/src/launchpad.net/goyaml/goyaml.go (about)

     1  // Package goyaml implements YAML support for the Go language.
     2  //
     3  // WARNING: You are using an out of date import path. Please update your code and import the following instead:
     4  //
     5  //   gonuts.org/v1/yaml
     6  //
     7  // The package name has changed from "yaml" from "goyaml", but the package API has not changed.
     8  //
     9  package goyaml
    10  
    11  import (
    12  	"errors"
    13  	"fmt"
    14  	"reflect"
    15  	"runtime"
    16  	"strings"
    17  	"sync"
    18  )
    19  
    20  func handleErr(err *error) {
    21  	if r := recover(); r != nil {
    22  		if _, ok := r.(runtime.Error); ok {
    23  			panic(r)
    24  		} else if _, ok := r.(*reflect.ValueError); ok {
    25  			panic(r)
    26  		} else if _, ok := r.(externalPanic); ok {
    27  			panic(r)
    28  		} else if s, ok := r.(string); ok {
    29  			*err = errors.New("YAML error: " + s)
    30  		} else if e, ok := r.(error); ok {
    31  			*err = e
    32  		} else {
    33  			panic(r)
    34  		}
    35  	}
    36  }
    37  
    38  // Objects implementing the goyaml.Setter interface will receive the YAML
    39  // tag and value via the SetYAML method during unmarshaling, rather than
    40  // being implicitly assigned by the goyaml machinery.  If setting the value
    41  // works, the method should return true.  If it returns false, the given
    42  // value will be omitted from maps and slices.
    43  type Setter interface {
    44  	SetYAML(tag string, value interface{}) bool
    45  }
    46  
    47  // Objects implementing the goyaml.Getter interface will get the GetYAML()
    48  // method called when goyaml is requested to marshal the given value, and
    49  // the result of this method will be marshaled in place of the actual object.
    50  type Getter interface {
    51  	GetYAML() (tag string, value interface{})
    52  }
    53  
    54  // Unmarshal decodes the first document found within the in byte slice
    55  // and assigns decoded values into the object pointed by out.
    56  //
    57  // Maps, pointers to structs and ints, etc, may all be used as out values.
    58  // If an internal pointer within a struct is not initialized, goyaml
    59  // will initialize it if necessary for unmarshalling the provided data,
    60  // but the struct provided as out must not be a nil pointer.
    61  //
    62  // The type of the decoded values and the type of out will be considered,
    63  // and Unmarshal() will do the best possible job to unmarshal values
    64  // appropriately.  It is NOT considered an error, though, to skip values
    65  // because they are not available in the decoded YAML, or if they are not
    66  // compatible with the out value. To ensure something was properly
    67  // unmarshaled use a map or compare against the previous value for the
    68  // field (usually the zero value).
    69  //
    70  // Struct fields are only unmarshalled if they are exported (have an
    71  // upper case first letter), and will be unmarshalled using the field
    72  // name lowercased by default. When custom field names are desired, the
    73  // tag value may be used to tweak the name. Everything before the first
    74  // comma in the field tag will be used as the name. The values following
    75  // the comma are used to tweak the marshalling process (see Marshal).
    76  // Conflicting names result in a runtime error.
    77  //
    78  // For example:
    79  //
    80  //     type T struct {
    81  //         F int `yaml:"a,omitempty"`
    82  //         B int
    83  //     }
    84  //     var T t
    85  //     goyaml.Unmarshal([]byte("a: 1\nb: 2"), &t)
    86  //
    87  // See the documentation of Marshal for the format of tags and a list of
    88  // supported tag options.
    89  //
    90  func Unmarshal(in []byte, out interface{}) (err error) {
    91  	defer handleErr(&err)
    92  	d := newDecoder()
    93  	p := newParser(in)
    94  	defer p.destroy()
    95  	node := p.parse()
    96  	if node != nil {
    97  		d.unmarshal(node, reflect.ValueOf(out))
    98  	}
    99  	return nil
   100  }
   101  
   102  // Marshal serializes the value provided into a YAML document. The structure
   103  // of the generated document will reflect the structure of the value itself.
   104  // Maps, pointers to structs and ints, etc, may all be used as the in value.
   105  //
   106  // In the case of struct values, only exported fields will be serialized.
   107  // The lowercased field name is used as the key for each exported field,
   108  // but this behavior may be changed using the respective field tag.
   109  // The tag may also contain flags to tweak the marshalling behavior for
   110  // the field. Conflicting names result in a runtime error. The tag format
   111  // accepted is:
   112  //
   113  //     `(...) yaml:"[<key>][,<flag1>[,<flag2>]]" (...)`
   114  //
   115  // The following flags are currently supported:
   116  //
   117  //     omitempty    Only include the field if it's not set to the zero
   118  //                  value for the type or to empty slices or maps.
   119  //                  Does not apply to zero valued structs.
   120  //
   121  //     flow         Marshal using a flow style (useful for structs,
   122  //                  sequences and maps.
   123  //
   124  //     inline       Inline the struct it's applied to, so its fields
   125  //                  are processed as if they were part of the outer
   126  //                  struct.
   127  //
   128  // In addition, if the key is "-", the field is ignored.
   129  //
   130  // For example:
   131  //
   132  //     type T struct {
   133  //         F int "a,omitempty"
   134  //         B int
   135  //     }
   136  //     goyaml.Marshal(&T{B: 2}) // Returns "b: 2\n"
   137  //     goyaml.Marshal(&T{F: 1}} // Returns "a: 1\nb: 0\n"
   138  //
   139  func Marshal(in interface{}) (out []byte, err error) {
   140  	defer handleErr(&err)
   141  	e := newEncoder()
   142  	defer e.destroy()
   143  	e.marshal("", reflect.ValueOf(in))
   144  	e.finish()
   145  	out = e.out
   146  	return
   147  }
   148  
   149  // --------------------------------------------------------------------------
   150  // Maintain a mapping of keys to structure field indexes
   151  
   152  // The code in this section was copied from gobson.
   153  
   154  // structInfo holds details for the serialization of fields of
   155  // a given struct.
   156  type structInfo struct {
   157  	FieldsMap  map[string]fieldInfo
   158  	FieldsList []fieldInfo
   159  
   160  	// InlineMap is the number of the field in the struct that
   161  	// contains an ,inline map, or -1 if there's none.
   162  	InlineMap int
   163  }
   164  
   165  type fieldInfo struct {
   166  	Key       string
   167  	Num       int
   168  	OmitEmpty bool
   169  	Flow      bool
   170  
   171  	// Inline holds the field index if the field is part of an inlined struct.
   172  	Inline []int
   173  }
   174  
   175  var structMap = make(map[reflect.Type]*structInfo)
   176  var fieldMapMutex sync.RWMutex
   177  
   178  type externalPanic string
   179  
   180  func (e externalPanic) String() string {
   181  	return string(e)
   182  }
   183  
   184  func getStructInfo(st reflect.Type) (*structInfo, error) {
   185  	fieldMapMutex.RLock()
   186  	sinfo, found := structMap[st]
   187  	fieldMapMutex.RUnlock()
   188  	if found {
   189  		return sinfo, nil
   190  	}
   191  
   192  	n := st.NumField()
   193  	fieldsMap := make(map[string]fieldInfo)
   194  	fieldsList := make([]fieldInfo, 0, n)
   195  	inlineMap := -1
   196  	for i := 0; i != n; i++ {
   197  		field := st.Field(i)
   198  		if field.PkgPath != "" {
   199  			continue // Private field
   200  		}
   201  
   202  		info := fieldInfo{Num: i}
   203  
   204  		tag := field.Tag.Get("yaml")
   205  		if tag == "" && strings.Index(string(field.Tag), ":") < 0 {
   206  			tag = string(field.Tag)
   207  		}
   208  		if tag == "-" {
   209  			continue
   210  		}
   211  
   212  		inline := false
   213  		fields := strings.Split(tag, ",")
   214  		if len(fields) > 1 {
   215  			for _, flag := range fields[1:] {
   216  				switch flag {
   217  				case "omitempty":
   218  					info.OmitEmpty = true
   219  				case "flow":
   220  					info.Flow = true
   221  				case "inline":
   222  					inline = true
   223  				default:
   224  					msg := fmt.Sprintf("Unsupported flag %q in tag %q of type %s", flag, tag, st)
   225  					panic(externalPanic(msg))
   226  				}
   227  			}
   228  			tag = fields[0]
   229  		}
   230  
   231  		if inline {
   232  			switch field.Type.Kind() {
   233  			//case reflect.Map:
   234  			//	if inlineMap >= 0 {
   235  			//		return nil, errors.New("Multiple ,inline maps in struct " + st.String())
   236  			//	}
   237  			//	if field.Type.Key() != reflect.TypeOf("") {
   238  			//		return nil, errors.New("Option ,inline needs a map with string keys in struct " + st.String())
   239  			//	}
   240  			//	inlineMap = info.Num
   241  			case reflect.Struct:
   242  				sinfo, err := getStructInfo(field.Type)
   243  				if err != nil {
   244  					return nil, err
   245  				}
   246  				for _, finfo := range sinfo.FieldsList {
   247  					if _, found := fieldsMap[finfo.Key]; found {
   248  						msg := "Duplicated key '" + finfo.Key + "' in struct " + st.String()
   249  						return nil, errors.New(msg)
   250  					}
   251  					if finfo.Inline == nil {
   252  						finfo.Inline = []int{i, finfo.Num}
   253  					} else {
   254  						finfo.Inline = append([]int{i}, finfo.Inline...)
   255  					}
   256  					fieldsMap[finfo.Key] = finfo
   257  					fieldsList = append(fieldsList, finfo)
   258  				}
   259  			default:
   260  				//panic("Option ,inline needs a struct value or map field")
   261  				panic("Option ,inline needs a struct value field")
   262  			}
   263  			continue
   264  		}
   265  
   266  		if tag != "" {
   267  			info.Key = tag
   268  		} else {
   269  			info.Key = strings.ToLower(field.Name)
   270  		}
   271  
   272  		if _, found = fieldsMap[info.Key]; found {
   273  			msg := "Duplicated key '" + info.Key + "' in struct " + st.String()
   274  			return nil, errors.New(msg)
   275  		}
   276  
   277  		fieldsList = append(fieldsList, info)
   278  		fieldsMap[info.Key] = info
   279  	}
   280  
   281  	sinfo = &structInfo{fieldsMap, fieldsList, inlineMap}
   282  
   283  	fieldMapMutex.Lock()
   284  	structMap[st] = sinfo
   285  	fieldMapMutex.Unlock()
   286  	return sinfo, nil
   287  }
   288  
   289  func isZero(v reflect.Value) bool {
   290  	switch v.Kind() {
   291  	case reflect.String:
   292  		return len(v.String()) == 0
   293  	case reflect.Interface, reflect.Ptr:
   294  		return v.IsNil()
   295  	case reflect.Slice:
   296  		return v.Len() == 0
   297  	case reflect.Map:
   298  		return v.Len() == 0
   299  	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
   300  		return v.Int() == 0
   301  	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
   302  		return v.Uint() == 0
   303  	case reflect.Bool:
   304  		return !v.Bool()
   305  	}
   306  	return false
   307  }