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 }