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