github.com/gogf/gf@v1.16.9/internal/structs/structs_field.go (about)

     1  // Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
     2  //
     3  // This Source Code Form is subject to the terms of the MIT License.
     4  // If a copy of the MIT was not distributed with this file,
     5  // You can obtain one at https://github.com/gogf/gf.
     6  
     7  package structs
     8  
     9  import "reflect"
    10  
    11  // Tag returns the value associated with key in the tag string. If there is no
    12  // such key in the tag, Tag returns the empty string.
    13  func (f *Field) Tag(key string) string {
    14  	return f.Field.Tag.Get(key)
    15  }
    16  
    17  // TagLookup returns the value associated with key in the tag string.
    18  // If the key is present in the tag the value (which may be empty)
    19  // is returned. Otherwise, the returned value will be the empty string.
    20  // The ok return value reports whether the value was explicitly set in
    21  // the tag string. If the tag does not have the conventional format,
    22  // the value returned by Lookup is unspecified.
    23  func (f *Field) TagLookup(key string) (value string, ok bool) {
    24  	return f.Field.Tag.Lookup(key)
    25  }
    26  
    27  // IsEmbedded returns true if the given field is an anonymous field (embedded)
    28  func (f *Field) IsEmbedded() bool {
    29  	return f.Field.Anonymous
    30  }
    31  
    32  // TagStr returns the tag string of the field.
    33  func (f *Field) TagStr() string {
    34  	return string(f.Field.Tag)
    35  }
    36  
    37  // IsExported returns true if the given field is exported.
    38  func (f *Field) IsExported() bool {
    39  	return f.Field.PkgPath == ""
    40  }
    41  
    42  // Name returns the name of the given field
    43  func (f *Field) Name() string {
    44  	return f.Field.Name
    45  }
    46  
    47  // Type returns the type of the given field
    48  func (f *Field) Type() Type {
    49  	return Type{
    50  		Type: f.Field.Type,
    51  	}
    52  }
    53  
    54  // Kind returns the reflect.Kind for Value of Field `f`.
    55  func (f *Field) Kind() reflect.Kind {
    56  	return f.Value.Kind()
    57  }
    58  
    59  // OriginalKind retrieves and returns the original reflect.Kind for Value of Field `f`.
    60  func (f *Field) OriginalKind() reflect.Kind {
    61  	var (
    62  		kind  = f.Value.Kind()
    63  		value = f.Value
    64  	)
    65  	for kind == reflect.Ptr {
    66  		value = value.Elem()
    67  		kind = value.Kind()
    68  	}
    69  	return kind
    70  }
    71  
    72  const (
    73  	RecursiveOptionNone          = 0 // No recursively retrieving fields as map if the field is an embedded struct.
    74  	RecursiveOptionEmbedded      = 1 // Recursively retrieving fields as map if the field is an embedded struct.
    75  	RecursiveOptionEmbeddedNoTag = 2 // Recursively retrieving fields as map if the field is an embedded struct and the field has no tag.
    76  )
    77  
    78  type FieldMapInput struct {
    79  	// Pointer should be type of struct/*struct.
    80  	Pointer interface{}
    81  
    82  	// PriorityTagArray specifies the priority tag array for retrieving from high to low.
    83  	// If it's given `nil`, it returns map[name]*Field, of which the `name` is attribute name.
    84  	PriorityTagArray []string
    85  
    86  	// RecursiveOption specifies the way retrieving the fields recursively if the attribute
    87  	// is an embedded struct. It is RecursiveOptionNone in default.
    88  	RecursiveOption int
    89  }
    90  
    91  // FieldMap retrieves and returns struct field as map[name/tag]*Field from `pointer`.
    92  //
    93  // The parameter `pointer` should be type of struct/*struct.
    94  //
    95  // The parameter `priority` specifies the priority tag array for retrieving from high to low.
    96  // If it's given `nil`, it returns map[name]*Field, of which the `name` is attribute name.
    97  //
    98  // The parameter `recursive` specifies the whether retrieving the fields recursively if the attribute
    99  // is an embedded struct.
   100  //
   101  // Note that it only retrieves the exported attributes with first letter up-case from struct.
   102  func FieldMap(input FieldMapInput) (map[string]*Field, error) {
   103  	fields, err := getFieldValues(input.Pointer)
   104  	if err != nil {
   105  		return nil, err
   106  	}
   107  	var (
   108  		tagValue = ""
   109  		mapField = make(map[string]*Field)
   110  	)
   111  	for _, field := range fields {
   112  		// Only retrieve exported attributes.
   113  		if !field.IsExported() {
   114  			continue
   115  		}
   116  		tagValue = ""
   117  		for _, p := range input.PriorityTagArray {
   118  			tagValue = field.Tag(p)
   119  			if tagValue != "" && tagValue != "-" {
   120  				break
   121  			}
   122  		}
   123  		tempField := field
   124  		tempField.TagValue = tagValue
   125  		if tagValue != "" {
   126  			mapField[tagValue] = tempField
   127  		} else {
   128  			if input.RecursiveOption != RecursiveOptionNone && field.IsEmbedded() {
   129  				switch input.RecursiveOption {
   130  				case RecursiveOptionEmbeddedNoTag:
   131  					if field.TagStr() != "" {
   132  						mapField[field.Name()] = tempField
   133  						break
   134  					}
   135  					fallthrough
   136  				case RecursiveOptionEmbedded:
   137  					m, err := FieldMap(FieldMapInput{
   138  						Pointer:          field.Value,
   139  						PriorityTagArray: input.PriorityTagArray,
   140  						RecursiveOption:  input.RecursiveOption,
   141  					})
   142  					if err != nil {
   143  						return nil, err
   144  					}
   145  					for k, v := range m {
   146  						if _, ok := mapField[k]; !ok {
   147  							tempV := v
   148  							mapField[k] = tempV
   149  						}
   150  					}
   151  				}
   152  			} else {
   153  				mapField[field.Name()] = tempField
   154  			}
   155  		}
   156  	}
   157  	return mapField, nil
   158  }