github.com/gogf/gf@v1.16.9/internal/structs/structs_tag.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 (
    10  	"errors"
    11  	"reflect"
    12  	"strconv"
    13  )
    14  
    15  // ParseTag parses tag string into map.
    16  func ParseTag(tag string) map[string]string {
    17  	var (
    18  		key  string
    19  		data = make(map[string]string)
    20  	)
    21  	for tag != "" {
    22  		// Skip leading space.
    23  		i := 0
    24  		for i < len(tag) && tag[i] == ' ' {
    25  			i++
    26  		}
    27  		tag = tag[i:]
    28  		if tag == "" {
    29  			break
    30  		}
    31  		// Scan to colon. A space, a quote or a control character is a syntax error.
    32  		// Strictly speaking, control chars include the range [0x7f, 0x9f], not just
    33  		// [0x00, 0x1f], but in practice, we ignore the multi-byte control characters
    34  		// as it is simpler to inspect the tag's bytes than the tag's runes.
    35  		i = 0
    36  		for i < len(tag) && tag[i] > ' ' && tag[i] != ':' && tag[i] != '"' && tag[i] != 0x7f {
    37  			i++
    38  		}
    39  		if i == 0 || i+1 >= len(tag) || tag[i] != ':' || tag[i+1] != '"' {
    40  			break
    41  		}
    42  		key = tag[:i]
    43  		tag = tag[i+1:]
    44  
    45  		// Scan quoted string to find value.
    46  		i = 1
    47  		for i < len(tag) && tag[i] != '"' {
    48  			if tag[i] == '\\' {
    49  				i++
    50  			}
    51  			i++
    52  		}
    53  		if i >= len(tag) {
    54  			break
    55  		}
    56  		quotedValue := tag[:i+1]
    57  		tag = tag[i+1:]
    58  		value, err := strconv.Unquote(quotedValue)
    59  		if err != nil {
    60  			panic(err)
    61  		}
    62  		data[key] = value
    63  	}
    64  	return data
    65  }
    66  
    67  // TagFields retrieves and returns struct tags as []*Field from `pointer`.
    68  //
    69  // The parameter `pointer` should be type of struct/*struct.
    70  //
    71  // Note that it only retrieves the exported attributes with first letter up-case from struct.
    72  func TagFields(pointer interface{}, priority []string) ([]*Field, error) {
    73  	return getFieldValuesByTagPriority(pointer, priority, map[string]struct{}{})
    74  }
    75  
    76  // TagMapName retrieves and returns struct tags as map[tag]attribute from `pointer`.
    77  //
    78  // The parameter `pointer` should be type of struct/*struct.
    79  //
    80  // Note that it only retrieves the exported attributes with first letter up-case from struct.
    81  func TagMapName(pointer interface{}, priority []string) (map[string]string, error) {
    82  	fields, err := TagFields(pointer, priority)
    83  	if err != nil {
    84  		return nil, err
    85  	}
    86  	tagMap := make(map[string]string, len(fields))
    87  	for _, field := range fields {
    88  		tagMap[field.TagValue] = field.Name()
    89  	}
    90  	return tagMap, nil
    91  }
    92  
    93  // TagMapField retrieves struct tags as map[tag]*Field from `pointer`, and returns it.
    94  // The parameter `object` should be either type of struct/*struct/[]struct/[]*struct.
    95  //
    96  // Note that it only retrieves the exported attributes with first letter up-case from struct.
    97  func TagMapField(object interface{}, priority []string) (map[string]*Field, error) {
    98  	fields, err := TagFields(object, priority)
    99  	if err != nil {
   100  		return nil, err
   101  	}
   102  	tagMap := make(map[string]*Field, len(fields))
   103  	for _, field := range fields {
   104  		tagField := field
   105  		tagMap[field.TagValue] = tagField
   106  	}
   107  	return tagMap, nil
   108  }
   109  
   110  func getFieldValues(value interface{}) ([]*Field, error) {
   111  	var (
   112  		reflectValue reflect.Value
   113  		reflectKind  reflect.Kind
   114  	)
   115  	if v, ok := value.(reflect.Value); ok {
   116  		reflectValue = v
   117  		reflectKind = reflectValue.Kind()
   118  	} else {
   119  		reflectValue = reflect.ValueOf(value)
   120  		reflectKind = reflectValue.Kind()
   121  	}
   122  	for {
   123  		switch reflectKind {
   124  		case reflect.Ptr:
   125  			if !reflectValue.IsValid() || reflectValue.IsNil() {
   126  				// If pointer is type of *struct and nil, then automatically create a temporary struct.
   127  				reflectValue = reflect.New(reflectValue.Type().Elem()).Elem()
   128  				reflectKind = reflectValue.Kind()
   129  			} else {
   130  				reflectValue = reflectValue.Elem()
   131  				reflectKind = reflectValue.Kind()
   132  			}
   133  		case reflect.Array, reflect.Slice:
   134  			reflectValue = reflect.New(reflectValue.Type().Elem()).Elem()
   135  			reflectKind = reflectValue.Kind()
   136  		default:
   137  			goto exitLoop
   138  		}
   139  	}
   140  
   141  exitLoop:
   142  	for reflectKind == reflect.Ptr {
   143  		reflectValue = reflectValue.Elem()
   144  		reflectKind = reflectValue.Kind()
   145  	}
   146  	if reflectKind != reflect.Struct {
   147  		return nil, errors.New("given value should be either type of struct/*struct/[]struct/[]*struct")
   148  	}
   149  	var (
   150  		structType = reflectValue.Type()
   151  		length     = reflectValue.NumField()
   152  		fields     = make([]*Field, length)
   153  	)
   154  	for i := 0; i < length; i++ {
   155  		fields[i] = &Field{
   156  			Value: reflectValue.Field(i),
   157  			Field: structType.Field(i),
   158  		}
   159  	}
   160  	return fields, nil
   161  }
   162  
   163  func getFieldValuesByTagPriority(pointer interface{}, priority []string, tagMap map[string]struct{}) ([]*Field, error) {
   164  	fields, err := getFieldValues(pointer)
   165  	if err != nil {
   166  		return nil, err
   167  	}
   168  	var (
   169  		tagValue  = ""
   170  		tagFields = make([]*Field, 0)
   171  	)
   172  	for _, field := range fields {
   173  		// Only retrieve exported attributes.
   174  		if !field.IsExported() {
   175  			continue
   176  		}
   177  		tagValue = ""
   178  		for _, p := range priority {
   179  			tagValue = field.Tag(p)
   180  			if tagValue != "" && tagValue != "-" {
   181  				break
   182  			}
   183  		}
   184  		if tagValue != "" {
   185  			// Filter repeated tag.
   186  			if _, ok := tagMap[tagValue]; ok {
   187  				continue
   188  			}
   189  			tagField := field
   190  			tagField.TagValue = tagValue
   191  			tagFields = append(tagFields, tagField)
   192  		}
   193  		// If this is an embedded attribute, it retrieves the tags recursively.
   194  		if field.IsEmbedded() {
   195  			if subTagFields, err := getFieldValuesByTagPriority(field.Value, priority, tagMap); err != nil {
   196  				return nil, err
   197  			} else {
   198  				tagFields = append(tagFields, subTagFields...)
   199  			}
   200  		}
   201  	}
   202  	return tagFields, nil
   203  }