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