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  }