github.com/wfusion/gofusion@v1.1.14/common/utils/reflect.go (about)

     1  package utils
     2  
     3  import (
     4  	"container/list"
     5  	"reflect"
     6  	"regexp"
     7  	"strings"
     8  
     9  	"gorm.io/gorm/schema"
    10  )
    11  
    12  // IsBlank gets whether the specified object is considered empty or not.
    13  // fork from: github.com/stretchr/testify@v1.8.0/assert/assertions.go
    14  func IsBlank(object any) bool {
    15  	// get nil case out of the way
    16  	if object == nil {
    17  		return true
    18  	}
    19  
    20  	objVal, ok := object.(reflect.Value)
    21  	if !ok {
    22  		objVal = reflect.ValueOf(object)
    23  	}
    24  	switch objVal.Kind() {
    25  	// collection types are empty when they have no element
    26  	case reflect.Chan, reflect.Map, reflect.Slice:
    27  		return objVal.Len() == 0
    28  	// pointers are empty if nil or if the value they point to is empty
    29  	case reflect.Ptr:
    30  		if objVal.IsNil() {
    31  			return true
    32  		}
    33  		deref := objVal.Elem().Interface()
    34  		return IsBlank(deref)
    35  	// for all other types, compare against the zero value
    36  	// array types are empty when they match their zero-initialized state
    37  	default:
    38  		zero := reflect.Zero(objVal.Type())
    39  		return reflect.DeepEqual(objVal.Interface(), zero.Interface())
    40  	}
    41  }
    42  
    43  func TraverseValue(data any, indirect bool, handler func(reflect.StructField, reflect.Value) (end, stepIn bool)) {
    44  	v, ok := data.(reflect.Value)
    45  	if !ok {
    46  		v = reflect.ValueOf(data)
    47  	}
    48  	v = IndirectValue(v)
    49  	l := list.New()
    50  	l.PushBack(v)
    51  TraverseStruct:
    52  	for l.Len() > 0 {
    53  		e := IndirectValue(l.Remove(l.Front()).(reflect.Value))
    54  		if !e.IsValid() {
    55  			continue
    56  		}
    57  		t := IndirectType(e.Type())
    58  		switch e.Kind() {
    59  		case reflect.Array, reflect.Slice:
    60  			for i, num := 0, e.Len(); i < num; i++ {
    61  				l.PushBack(e.Index(i))
    62  			}
    63  		case reflect.Map:
    64  			for iter := e.MapRange(); iter.Next(); {
    65  				l.PushBack(iter.Key())
    66  				l.PushBack(iter.Value())
    67  			}
    68  		case reflect.Struct:
    69  			for i, num := 0, e.NumField(); i < num; i++ {
    70  				ff := t.Field(i)
    71  				fv := e.Field(i)
    72  				if !fv.IsValid() {
    73  					continue
    74  				}
    75  				if indirect {
    76  					fv = IndirectValue(fv)
    77  					ff.Type = IndirectType(ff.Type)
    78  				}
    79  				end, stepIn := handler(ff, fv)
    80  				if end {
    81  					break TraverseStruct
    82  				}
    83  				if stepIn {
    84  					l.PushBack(fv)
    85  				}
    86  			}
    87  		default:
    88  			// do nothing
    89  		}
    90  	}
    91  }
    92  
    93  func GetFieldByTag(data any, tag, key string) (r reflect.Value, e error) {
    94  	TraverseValue(data, true, func(field reflect.StructField, value reflect.Value) (end, stepIn bool) {
    95  		if !value.IsValid() {
    96  			return false, false
    97  		}
    98  		if value.Type().Kind() == reflect.Struct {
    99  			return false, true
   100  		}
   101  		tagV := field.Tag.Get(tag)
   102  		if tagV == key {
   103  			r = value
   104  			end = true
   105  			return
   106  		}
   107  		return
   108  	})
   109  	return
   110  }
   111  
   112  func GetFieldByTagWithKeys(data any, tag string, keys []string) (r reflect.Value, e error) {
   113  	keySet := NewSet[string](keys...)
   114  	TraverseValue(data, true, func(field reflect.StructField, value reflect.Value) (end, stepIn bool) {
   115  		if !value.IsValid() {
   116  			return false, false
   117  		}
   118  		if value.Type().Kind() == reflect.Struct {
   119  			return false, true
   120  		}
   121  		if keySet.Contains(field.Tag.Get(tag)) {
   122  			r = value
   123  			end = true
   124  			return
   125  		}
   126  		return
   127  	})
   128  	return
   129  }
   130  
   131  func GetFieldTagValue(data any, tag string, pattern *regexp.Regexp) (tagValue string, e error) {
   132  	TraverseValue(data, true, func(field reflect.StructField, value reflect.Value) (end, stepIn bool) {
   133  		if !value.IsValid() {
   134  			return false, false
   135  		}
   136  		if value.Type().Kind() == reflect.Struct {
   137  			return false, true
   138  		}
   139  		tagV := field.Tag.Get(tag)
   140  		if pattern.Match([]byte(tagV)) {
   141  			tagValue = tagV
   142  			end = true
   143  			return
   144  		}
   145  		return
   146  	})
   147  	return
   148  }
   149  
   150  func GetGormColumnValue(data any, column string) (columnVal reflect.Value, ok bool) {
   151  	tagKey := strings.ToUpper(column)
   152  	TraverseValue(data, true, func(field reflect.StructField, value reflect.Value) (end, stepIn bool) {
   153  		if !value.IsValid() {
   154  			return false, false
   155  		}
   156  		if value.Type().Kind() == reflect.Struct {
   157  			return false, true
   158  		}
   159  		tagSetting := schema.ParseTagSetting(field.Tag.Get("gorm"), ";")
   160  		if _, ok := tagSetting[tagKey]; ok || tagSetting["COLUMN"] == column {
   161  			columnVal = value
   162  			end = true
   163  			return
   164  		}
   165  		return
   166  	})
   167  	return
   168  }
   169  
   170  // EmbedsType Returns true if t embeds e or if any of the types embedded by t embed e.
   171  // Forked from go.uber.org/dig@v1.16.1/inout.embedsType
   172  func EmbedsType(i any, e reflect.Type) bool {
   173  	// given `type A foo { *In }`, this function would return false for
   174  	// embedding dig.In, which makes for some extra error checking in places
   175  	// that call this function. Might be worthwhile to consider reflect.Indirect
   176  	// usage to clean up the callers.
   177  
   178  	if i == nil {
   179  		return false
   180  	}
   181  
   182  	// maybe it's already a reflect.Type
   183  	t, ok := i.(reflect.Type)
   184  	if !ok {
   185  		// take the type if it's not
   186  		t = IndirectType(reflect.TypeOf(i))
   187  	}
   188  
   189  	// We are going to do a breadth-first search of all embedded fields.
   190  	types := list.New()
   191  	types.PushBack(t)
   192  	for types.Len() > 0 {
   193  		t := types.Remove(types.Front()).(reflect.Type)
   194  
   195  		if t == e {
   196  			return true
   197  		}
   198  
   199  		if t.Kind() != reflect.Struct {
   200  			continue
   201  		}
   202  
   203  		for i := 0; i < t.NumField(); i++ {
   204  			f := t.Field(i)
   205  			if f.Anonymous {
   206  				types.PushBack(f.Type)
   207  			}
   208  		}
   209  	}
   210  
   211  	return false
   212  }
   213  
   214  func IndirectValue(s reflect.Value) (d reflect.Value) {
   215  	if !s.IsValid() {
   216  		return s
   217  	}
   218  	d = s
   219  	for d.Kind() == reflect.Ptr {
   220  		d = d.Elem()
   221  	}
   222  	return
   223  }
   224  
   225  func IndirectType(s reflect.Type) (d reflect.Type) {
   226  	if s == nil {
   227  		return s
   228  	}
   229  	d = s
   230  	for d.Kind() == reflect.Ptr {
   231  		d = d.Elem()
   232  	}
   233  	return
   234  }