github.com/keysonzzz/kmg@v0.0.0-20151121023212-05317bfd7d39/encoding/kmgYaml/goyaml.go (about)

     1  // Package goyaml implements YAML support for the Go language.
     2  package kmgYaml
     3  
     4  import (
     5  	"errors"
     6  	"fmt"
     7  	"reflect"
     8  	"runtime"
     9  	"strings"
    10  	"sync"
    11  )
    12  
    13  func handleErr(err *error) {
    14  	if r := recover(); r != nil {
    15  		if _, ok := r.(runtime.Error); ok {
    16  			panic(r)
    17  		} else if _, ok := r.(*reflect.ValueError); ok {
    18  			panic(r)
    19  		} else if _, ok := r.(externalPanic); ok {
    20  			panic(r)
    21  		} else if s, ok := r.(string); ok {
    22  			*err = errors.New("YAML error: " + s)
    23  		} else if e, ok := r.(error); ok {
    24  			*err = e
    25  		} else {
    26  			panic(r)
    27  		}
    28  	}
    29  }
    30  
    31  // Objects implementing the goyaml.Setter interface will receive the YAML
    32  // tag and value via the SetYAML method during unmarshaling, rather than
    33  // being implicitly assigned by the goyaml machinery.  If setting the value
    34  // works, the method should return true.  If it returns false, the given
    35  // value will be omitted from maps and slices.
    36  type Setter interface {
    37  	SetYAML(tag string, value interface{}) bool
    38  }
    39  
    40  // Objects implementing the goyaml.Getter interface will get the GetYAML()
    41  // method called when goyaml is requested to marshal the given value, and
    42  // the result of this method will be marshaled in place of the actual object.
    43  type Getter interface {
    44  	GetYAML() (tag string, value interface{})
    45  }
    46  
    47  // Unmarshal decodes the first document found within the in byte slice
    48  // and assigns decoded values into the object pointed by out.
    49  //
    50  // Maps, pointers to structs and ints, etc, may all be used as out values.
    51  // If an internal pointer within a struct is not initialized, goyaml
    52  // will initialize it if necessary for unmarshalling the provided data,
    53  // but the struct provided as out must not be a nil pointer.
    54  //
    55  // The type of the decoded values and the type of out will be considered,
    56  // and Unmarshal() will do the best possible job to unmarshal values
    57  // appropriately.  It is NOT considered an error, though, to skip values
    58  // because they are not available in the decoded YAML, or if they are not
    59  // compatible with the out value. To ensure something was properly
    60  // unmarshaled use a map or compare against the previous value for the
    61  // field (usually the zero value).
    62  //
    63  // Struct fields are only unmarshalled if they are exported (have an
    64  // upper case first letter), and will be unmarshalled using the field
    65  // name lowercased by default. When custom field names are desired, the
    66  // tag value may be used to tweak the name. Everything before the first
    67  // comma in the field tag will be used as the name. The values following
    68  // the comma are used to tweak the marshalling process (see Marshal).
    69  // Conflicting names result in a runtime error.
    70  //
    71  // For example:
    72  //
    73  //     type T struct {
    74  //         F int `yaml:"a,omitempty"`
    75  //         B int
    76  //     }
    77  //     var T t
    78  //     goyaml.Unmarshal([]byte("a: 1\nb: 2"), &t)
    79  //
    80  // See the documentation of Marshal for the format of tags and a list of
    81  // supported tag options.
    82  //
    83  func Unmarshal(in []byte, out interface{}) (err error) {
    84  	defer handleErr(&err)
    85  	d := newDecoder()
    86  	p := newParser(in)
    87  	defer p.destroy()
    88  	node := p.parse()
    89  	if node != nil {
    90  		val := reflect.ValueOf(out)
    91  		switch val.Kind() {
    92  		case reflect.Ptr, reflect.Map:
    93  		default:
    94  			return errors.New("YAML error: Unmarshal need a pointer or a map.")
    95  		}
    96  		d.unmarshal(node, val)
    97  	}
    98  	return nil
    99  }
   100  
   101  // Marshal serializes the value provided into a YAML document. The structure
   102  // of the generated document will reflect the structure of the value itself.
   103  // Maps, pointers to structs and ints, etc, may all be used as the in value.
   104  //
   105  // In the case of struct values, only exported fields will be serialized.
   106  // The lowercased field name is used as the key for each exported field,
   107  // but this behavior may be changed using the respective field tag.
   108  // The tag may also contain flags to tweak the marshalling behavior for
   109  // the field. Conflicting names result in a runtime error. The tag format
   110  // 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  //     goyaml.Marshal(&T{B: 2}) // Returns "b: 2\n"
   136  //     goyaml.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  	yaml_emitter_set_unicode(&e.emitter, true)
   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  		if field.Anonymous {
   231  			inline = true
   232  		}
   233  		if inline {
   234  			switch field.Type.Kind() {
   235  			//case reflect.Map:
   236  			//	if inlineMap >= 0 {
   237  			//		return nil, errors.New("Multiple ,inline maps in struct " + st.String())
   238  			//	}
   239  			//	if field.Type.Key() != reflect.TypeOf("") {
   240  			//		return nil, errors.New("Option ,inline needs a map with string keys in struct " + st.String())
   241  			//	}
   242  			//	inlineMap = info.Num
   243  			case reflect.Struct:
   244  				sinfo, err := getStructInfo(field.Type)
   245  				if err != nil {
   246  					return nil, err
   247  				}
   248  				for _, finfo := range sinfo.FieldsList {
   249  					if _, found := fieldsMap[finfo.Key]; found {
   250  						msg := "Duplicated key '" + finfo.Key + "' in struct " + st.String()
   251  						return nil, errors.New(msg)
   252  					}
   253  					if finfo.Inline == nil {
   254  						finfo.Inline = []int{i, finfo.Num}
   255  					} else {
   256  						finfo.Inline = append([]int{i}, finfo.Inline...)
   257  					}
   258  					fieldsMap[finfo.Key] = finfo
   259  					fieldsList = append(fieldsList, finfo)
   260  				}
   261  			default:
   262  				//panic("Option ,inline needs a struct value or map field")
   263  				panic("Option ,inline needs a struct value field")
   264  			}
   265  			continue
   266  		}
   267  
   268  		if tag != "" {
   269  			info.Key = tag
   270  		} else {
   271  			info.Key = field.Name
   272  		}
   273  
   274  		if _, found = fieldsMap[info.Key]; found {
   275  			msg := "Duplicated key '" + info.Key + "' in struct " + st.String()
   276  			return nil, errors.New(msg)
   277  		}
   278  
   279  		fieldsList = append(fieldsList, info)
   280  		fieldsMap[info.Key] = info
   281  	}
   282  
   283  	sinfo = &structInfo{fieldsMap, fieldsList, inlineMap}
   284  
   285  	fieldMapMutex.Lock()
   286  	structMap[st] = sinfo
   287  	fieldMapMutex.Unlock()
   288  	return sinfo, nil
   289  }
   290  
   291  func isZero(v reflect.Value) bool {
   292  	switch v.Kind() {
   293  	case reflect.String:
   294  		return len(v.String()) == 0
   295  	case reflect.Interface, reflect.Ptr:
   296  		return v.IsNil()
   297  	case reflect.Slice:
   298  		return v.Len() == 0
   299  	case reflect.Map:
   300  		return v.Len() == 0
   301  	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
   302  		return v.Int() == 0
   303  	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
   304  		return v.Uint() == 0
   305  	case reflect.Bool:
   306  		return !v.Bool()
   307  	}
   308  	return false
   309  }