struct.go raw

   1  package yaml
   2  
   3  import (
   4  	"reflect"
   5  	"strings"
   6  
   7  	"golang.org/x/xerrors"
   8  )
   9  
  10  const (
  11  	// StructTagName tag keyword for Marshal/Unmarshal
  12  	StructTagName = "yaml"
  13  )
  14  
  15  // StructField information for each the field in structure
  16  type StructField struct {
  17  	FieldName    string
  18  	RenderName   string
  19  	AnchorName   string
  20  	AliasName    string
  21  	IsAutoAnchor bool
  22  	IsAutoAlias  bool
  23  	IsOmitEmpty  bool
  24  	IsFlow       bool
  25  	IsInline     bool
  26  }
  27  
  28  func getTag(field reflect.StructField) string {
  29  	// If struct tag `yaml` exist, use that. If no `yaml`
  30  	// exists, but `json` does, use that and try the best to
  31  	// adhere to its rules
  32  	tag := field.Tag.Get(StructTagName)
  33  	if tag == "" {
  34  		tag = field.Tag.Get(`json`)
  35  	}
  36  	return tag
  37  }
  38  
  39  func structField(field reflect.StructField) *StructField {
  40  	tag := getTag(field)
  41  	fieldName := strings.ToLower(field.Name)
  42  	options := strings.Split(tag, ",")
  43  	if len(options) > 0 {
  44  		if options[0] != "" {
  45  			fieldName = options[0]
  46  		}
  47  	}
  48  	structField := &StructField{
  49  		FieldName:  field.Name,
  50  		RenderName: fieldName,
  51  	}
  52  	if len(options) > 1 {
  53  		for _, opt := range options[1:] {
  54  			switch {
  55  			case opt == "omitempty":
  56  				structField.IsOmitEmpty = true
  57  			case opt == "flow":
  58  				structField.IsFlow = true
  59  			case opt == "inline":
  60  				structField.IsInline = true
  61  			case strings.HasPrefix(opt, "anchor"):
  62  				anchor := strings.Split(opt, "=")
  63  				if len(anchor) > 1 {
  64  					structField.AnchorName = anchor[1]
  65  				} else {
  66  					structField.IsAutoAnchor = true
  67  				}
  68  			case strings.HasPrefix(opt, "alias"):
  69  				alias := strings.Split(opt, "=")
  70  				if len(alias) > 1 {
  71  					structField.AliasName = alias[1]
  72  				} else {
  73  					structField.IsAutoAlias = true
  74  				}
  75  			default:
  76  			}
  77  		}
  78  	}
  79  	return structField
  80  }
  81  
  82  func isIgnoredStructField(field reflect.StructField) bool {
  83  	if field.PkgPath != "" && !field.Anonymous {
  84  		// private field
  85  		return true
  86  	}
  87  	tag := getTag(field)
  88  	if tag == "-" {
  89  		return true
  90  	}
  91  	return false
  92  }
  93  
  94  type StructFieldMap map[string]*StructField
  95  
  96  func (m StructFieldMap) isIncludedRenderName(name string) bool {
  97  	for _, v := range m {
  98  		if !v.IsInline && v.RenderName == name {
  99  			return true
 100  		}
 101  	}
 102  	return false
 103  }
 104  
 105  func (m StructFieldMap) hasMergeProperty() bool {
 106  	for _, v := range m {
 107  		if v.IsOmitEmpty && v.IsInline && v.IsAutoAlias {
 108  			return true
 109  		}
 110  	}
 111  	return false
 112  }
 113  
 114  func structFieldMap(structType reflect.Type) (StructFieldMap, error) {
 115  	structFieldMap := StructFieldMap{}
 116  	renderNameMap := map[string]struct{}{}
 117  	for i := 0; i < structType.NumField(); i++ {
 118  		field := structType.Field(i)
 119  		if isIgnoredStructField(field) {
 120  			continue
 121  		}
 122  		structField := structField(field)
 123  		if _, exists := renderNameMap[structField.RenderName]; exists {
 124  			return nil, xerrors.Errorf("duplicated struct field name %s", structField.RenderName)
 125  		}
 126  		structFieldMap[structField.FieldName] = structField
 127  		renderNameMap[structField.RenderName] = struct{}{}
 128  	}
 129  	return structFieldMap, nil
 130  }
 131