2021-01-17 21:46:25 +08:00
|
|
|
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
2019-07-03 22:09:35 +08:00
|
|
|
//
|
|
|
|
// This Source Code Form is subject to the terms of the MIT License.
|
|
|
|
// If a copy of the MIT was not distributed with this file,
|
|
|
|
// You can obtain one at https://github.com/gogf/gf.
|
|
|
|
|
2019-07-04 11:11:41 +08:00
|
|
|
package structs
|
2019-07-03 22:09:35 +08:00
|
|
|
|
2021-10-02 14:52:28 +08:00
|
|
|
import (
|
|
|
|
"reflect"
|
|
|
|
"regexp"
|
|
|
|
"strings"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
jsonTagName = `json`
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
tagMapRegex, _ = regexp.Compile(`([\w\-]+):"(.+?)"`)
|
|
|
|
)
|
2021-03-10 23:28:34 +08:00
|
|
|
|
2021-02-08 17:57:21 +08:00
|
|
|
// Tag returns the value associated with key in the tag string. If there is no
|
|
|
|
// such key in the tag, Tag returns the empty string.
|
|
|
|
func (f *Field) Tag(key string) string {
|
|
|
|
return f.Field.Tag.Get(key)
|
|
|
|
}
|
|
|
|
|
2021-10-02 14:52:28 +08:00
|
|
|
// TagJsonName returns the `json` tag name string of the field.
|
|
|
|
func (f *Field) TagJsonName() string {
|
|
|
|
if jsonTag := f.Tag(jsonTagName); jsonTag != "" {
|
|
|
|
return strings.Split(jsonTag, ",")[0]
|
|
|
|
}
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
2021-05-17 19:59:34 +08:00
|
|
|
// TagLookup returns the value associated with key in the tag string.
|
|
|
|
// If the key is present in the tag the value (which may be empty)
|
2021-08-08 13:10:21 +08:00
|
|
|
// is returned. Otherwise, the returned value will be the empty string.
|
2021-05-17 19:59:34 +08:00
|
|
|
// The ok return value reports whether the value was explicitly set in
|
|
|
|
// the tag string. If the tag does not have the conventional format,
|
|
|
|
// the value returned by Lookup is unspecified.
|
|
|
|
func (f *Field) TagLookup(key string) (value string, ok bool) {
|
|
|
|
return f.Field.Tag.Lookup(key)
|
|
|
|
}
|
|
|
|
|
2021-02-08 17:57:21 +08:00
|
|
|
// IsEmbedded returns true if the given field is an anonymous field (embedded)
|
|
|
|
func (f *Field) IsEmbedded() bool {
|
|
|
|
return f.Field.Anonymous
|
|
|
|
}
|
|
|
|
|
2021-08-03 22:21:20 +08:00
|
|
|
// TagStr returns the tag string of the field.
|
|
|
|
func (f *Field) TagStr() string {
|
|
|
|
return string(f.Field.Tag)
|
|
|
|
}
|
|
|
|
|
2021-10-02 14:52:28 +08:00
|
|
|
// TagMap returns all the tag of the field along with its value string as map.
|
|
|
|
func (f *Field) TagMap() map[string]string {
|
|
|
|
var (
|
|
|
|
data = map[string]string{}
|
|
|
|
match = tagMapRegex.FindAllStringSubmatch(f.TagStr(), -1)
|
|
|
|
)
|
|
|
|
for _, m := range match {
|
|
|
|
if len(m) == 3 {
|
|
|
|
data[m[1]] = m[2]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return data
|
|
|
|
}
|
|
|
|
|
2021-02-08 17:57:21 +08:00
|
|
|
// IsExported returns true if the given field is exported.
|
|
|
|
func (f *Field) IsExported() bool {
|
|
|
|
return f.Field.PkgPath == ""
|
|
|
|
}
|
|
|
|
|
|
|
|
// Name returns the name of the given field
|
|
|
|
func (f *Field) Name() string {
|
|
|
|
return f.Field.Name
|
|
|
|
}
|
|
|
|
|
|
|
|
// Type returns the type of the given field
|
|
|
|
func (f *Field) Type() Type {
|
|
|
|
return Type{
|
|
|
|
Type: f.Field.Type,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-10 23:28:34 +08:00
|
|
|
// Kind returns the reflect.Kind for Value of Field `f`.
|
|
|
|
func (f *Field) Kind() reflect.Kind {
|
|
|
|
return f.Value.Kind()
|
|
|
|
}
|
|
|
|
|
|
|
|
// OriginalKind retrieves and returns the original reflect.Kind for Value of Field `f`.
|
|
|
|
func (f *Field) OriginalKind() reflect.Kind {
|
|
|
|
var (
|
|
|
|
kind = f.Value.Kind()
|
|
|
|
value = f.Value
|
|
|
|
)
|
|
|
|
for kind == reflect.Ptr {
|
|
|
|
value = value.Elem()
|
|
|
|
kind = value.Kind()
|
|
|
|
}
|
|
|
|
return kind
|
|
|
|
}
|
|
|
|
|
2021-08-03 22:21:20 +08:00
|
|
|
const (
|
|
|
|
RecursiveOptionNone = 0 // No recursively retrieving fields as map if the field is an embedded struct.
|
|
|
|
RecursiveOptionEmbedded = 1 // Recursively retrieving fields as map if the field is an embedded struct.
|
|
|
|
RecursiveOptionEmbeddedNoTag = 2 // Recursively retrieving fields as map if the field is an embedded struct and the field has no tag.
|
|
|
|
)
|
|
|
|
|
2021-09-28 22:02:08 +08:00
|
|
|
type FieldsInput struct {
|
|
|
|
// Pointer should be type of struct/*struct.
|
|
|
|
Pointer interface{}
|
|
|
|
|
|
|
|
// RecursiveOption specifies the way retrieving the fields recursively if the attribute
|
|
|
|
// is an embedded struct. It is RecursiveOptionNone in default.
|
|
|
|
RecursiveOption int
|
|
|
|
}
|
|
|
|
|
2021-08-03 22:21:20 +08:00
|
|
|
type FieldMapInput struct {
|
|
|
|
// Pointer should be type of struct/*struct.
|
|
|
|
Pointer interface{}
|
|
|
|
|
|
|
|
// PriorityTagArray specifies the priority tag array for retrieving from high to low.
|
|
|
|
// If it's given `nil`, it returns map[name]*Field, of which the `name` is attribute name.
|
|
|
|
PriorityTagArray []string
|
|
|
|
|
|
|
|
// RecursiveOption specifies the way retrieving the fields recursively if the attribute
|
|
|
|
// is an embedded struct. It is RecursiveOptionNone in default.
|
|
|
|
RecursiveOption int
|
|
|
|
}
|
|
|
|
|
2021-09-28 22:02:08 +08:00
|
|
|
// Fields retrieves and returns the fields of `pointer` as slice.
|
|
|
|
func Fields(in FieldsInput) ([]*Field, error) {
|
|
|
|
var (
|
2021-10-26 21:57:56 +08:00
|
|
|
ok bool
|
|
|
|
fieldFilterMap = make(map[string]struct{})
|
|
|
|
retrievedFields = make([]*Field, 0)
|
|
|
|
currentLevelFieldMap = make(map[string]*Field)
|
2021-09-28 22:02:08 +08:00
|
|
|
)
|
|
|
|
rangeFields, err := getFieldValues(in.Pointer)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2021-10-26 21:57:56 +08:00
|
|
|
|
2021-10-06 11:10:35 +08:00
|
|
|
for index := 0; index < len(rangeFields); index++ {
|
2021-10-02 14:52:28 +08:00
|
|
|
field := rangeFields[index]
|
2021-10-26 21:57:56 +08:00
|
|
|
if !field.IsExported() {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
currentLevelFieldMap[field.Name()] = field
|
|
|
|
}
|
|
|
|
|
|
|
|
for index := 0; index < len(rangeFields); index++ {
|
|
|
|
field := rangeFields[index]
|
|
|
|
if _, ok = fieldFilterMap[field.Name()]; ok {
|
2021-09-28 22:02:08 +08:00
|
|
|
continue
|
|
|
|
}
|
2021-10-06 11:10:35 +08:00
|
|
|
// It only retrieves exported attributes.
|
2021-09-28 22:02:08 +08:00
|
|
|
if !field.IsExported() {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if field.IsEmbedded() {
|
|
|
|
if in.RecursiveOption != RecursiveOptionNone {
|
|
|
|
switch in.RecursiveOption {
|
|
|
|
case RecursiveOptionEmbeddedNoTag:
|
|
|
|
if field.TagStr() != "" {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
fallthrough
|
|
|
|
case RecursiveOptionEmbedded:
|
|
|
|
structFields, err := Fields(FieldsInput{
|
|
|
|
Pointer: field.Value,
|
|
|
|
RecursiveOption: in.RecursiveOption,
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2021-10-26 21:57:56 +08:00
|
|
|
// The current level fields can overwrite the sub-struct fields with the same name.
|
|
|
|
for i := 0; i < len(structFields); i++ {
|
|
|
|
var (
|
|
|
|
structField = structFields[i]
|
|
|
|
fieldName = structField.Name()
|
|
|
|
)
|
|
|
|
if _, ok = fieldFilterMap[fieldName]; ok {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
fieldFilterMap[fieldName] = struct{}{}
|
|
|
|
if v := currentLevelFieldMap[fieldName]; v == nil {
|
|
|
|
retrievedFields = append(retrievedFields, structField)
|
|
|
|
} else {
|
|
|
|
retrievedFields = append(retrievedFields, v)
|
|
|
|
}
|
|
|
|
}
|
2021-10-02 14:52:28 +08:00
|
|
|
continue
|
2021-09-28 22:02:08 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
continue
|
|
|
|
}
|
2021-10-26 21:57:56 +08:00
|
|
|
fieldFilterMap[field.Name()] = struct{}{}
|
2021-09-28 22:02:08 +08:00
|
|
|
retrievedFields = append(retrievedFields, field)
|
|
|
|
}
|
|
|
|
return retrievedFields, nil
|
|
|
|
}
|
|
|
|
|
2021-02-08 17:57:21 +08:00
|
|
|
// FieldMap retrieves and returns struct field as map[name/tag]*Field from `pointer`.
|
2020-01-01 14:57:57 +08:00
|
|
|
//
|
2021-02-08 17:57:21 +08:00
|
|
|
// The parameter `pointer` should be type of struct/*struct.
|
2020-04-09 13:37:27 +08:00
|
|
|
//
|
2021-02-08 17:57:21 +08:00
|
|
|
// The parameter `priority` specifies the priority tag array for retrieving from high to low.
|
|
|
|
// If it's given `nil`, it returns map[name]*Field, of which the `name` is attribute name.
|
2019-07-04 11:11:41 +08:00
|
|
|
//
|
2021-05-11 20:00:50 +08:00
|
|
|
// The parameter `recursive` specifies the whether retrieving the fields recursively if the attribute
|
|
|
|
// is an embedded struct.
|
|
|
|
//
|
2019-07-04 11:11:41 +08:00
|
|
|
// Note that it only retrieves the exported attributes with first letter up-case from struct.
|
2021-09-28 22:02:08 +08:00
|
|
|
func FieldMap(in FieldMapInput) (map[string]*Field, error) {
|
|
|
|
fields, err := getFieldValues(in.Pointer)
|
2020-11-08 14:25:17 +08:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2019-07-03 22:09:35 +08:00
|
|
|
}
|
2020-11-08 16:21:09 +08:00
|
|
|
var (
|
|
|
|
tagValue = ""
|
|
|
|
mapField = make(map[string]*Field)
|
|
|
|
)
|
|
|
|
for _, field := range fields {
|
|
|
|
// Only retrieve exported attributes.
|
|
|
|
if !field.IsExported() {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
tagValue = ""
|
2021-09-28 22:02:08 +08:00
|
|
|
for _, p := range in.PriorityTagArray {
|
2020-11-08 16:21:09 +08:00
|
|
|
tagValue = field.Tag(p)
|
|
|
|
if tagValue != "" && tagValue != "-" {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
tempField := field
|
|
|
|
tempField.TagValue = tagValue
|
|
|
|
if tagValue != "" {
|
|
|
|
mapField[tagValue] = tempField
|
|
|
|
} else {
|
2021-09-28 22:02:08 +08:00
|
|
|
if in.RecursiveOption != RecursiveOptionNone && field.IsEmbedded() {
|
|
|
|
switch in.RecursiveOption {
|
2021-08-03 22:21:20 +08:00
|
|
|
case RecursiveOptionEmbeddedNoTag:
|
|
|
|
if field.TagStr() != "" {
|
|
|
|
mapField[field.Name()] = tempField
|
|
|
|
break
|
|
|
|
}
|
|
|
|
fallthrough
|
|
|
|
case RecursiveOptionEmbedded:
|
|
|
|
m, err := FieldMap(FieldMapInput{
|
|
|
|
Pointer: field.Value,
|
2021-09-28 22:02:08 +08:00
|
|
|
PriorityTagArray: in.PriorityTagArray,
|
|
|
|
RecursiveOption: in.RecursiveOption,
|
2021-08-03 22:21:20 +08:00
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
for k, v := range m {
|
|
|
|
if _, ok := mapField[k]; !ok {
|
|
|
|
tempV := v
|
|
|
|
mapField[k] = tempV
|
|
|
|
}
|
2020-11-08 16:21:09 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
mapField[field.Name()] = tempField
|
|
|
|
}
|
2019-07-03 22:09:35 +08:00
|
|
|
}
|
|
|
|
}
|
2020-11-08 16:21:09 +08:00
|
|
|
return mapField, nil
|
2019-07-03 22:09:35 +08:00
|
|
|
}
|