github.com/codingeasygo/util@v0.0.0-20231206062002-1ce2f004b7d9/attrscan/attrscan.go (about) 1 package attrscan 2 3 import ( 4 "reflect" 5 "strings" 6 ) 7 8 type NilChecker interface { 9 IsNil() bool 10 } 11 12 type ZeroChecker interface { 13 IsZero() bool 14 } 15 16 type NameConv func(on, name string, field reflect.StructField) string 17 18 type Scanner struct { 19 Tag string 20 NameConv NameConv 21 } 22 23 var Default = &Scanner{ 24 Tag: "json", 25 NameConv: func(on, name string, field reflect.StructField) string { 26 return name 27 }, 28 } 29 30 func CheckValue(val reflect.Value, incNil, incZero bool) bool { 31 return Default.CheckValue(val, incNil, incZero) 32 } 33 34 func (s *Scanner) CheckValue(val reflect.Value, incNil, incZero bool) bool { 35 if !val.IsValid() { 36 return incNil 37 } 38 v := val.Interface() 39 if c, ok := v.(NilChecker); ok && c.IsNil() { 40 return incNil 41 } 42 if c, ok := v.(ZeroChecker); ok && c.IsZero() { 43 return incZero 44 } 45 if val.CanAddr() { 46 v := val.Addr().Interface() 47 // if c, ok := v.(NilChecker); ok && c.IsNil() { 48 // return incNil 49 // } 50 if c, ok := v.(ZeroChecker); ok && c.IsZero() { 51 return incZero 52 } 53 } 54 kind := val.Kind() 55 if kind == reflect.Ptr && val.IsNil() && !incNil { 56 return false 57 } 58 if kind == reflect.Ptr && !val.IsNil() { 59 val = reflect.Indirect(val) 60 } 61 if (!val.IsValid() || val.IsZero()) && !incZero { 62 return false 63 } 64 return true 65 } 66 67 func FilterFieldCall(on string, v interface{}, filter string, call func(fieldName, fieldFunc string, field reflect.StructField, value interface{})) { 68 Default.FilterFieldCall(on, v, filter, call) 69 } 70 71 func (s *Scanner) FilterFieldCall(on string, v interface{}, filter string, call func(fieldName, fieldFunc string, field reflect.StructField, value interface{})) { 72 for _, f := range strings.Split(filter, "|") { 73 s.filterFieldCall(on, v, f, call) 74 } 75 } 76 77 func (s *Scanner) filterFieldCall(on string, v interface{}, filter string, call func(fieldName, fieldFunc string, field reflect.StructField, value interface{})) { 78 reflectValue := reflect.Indirect(reflect.ValueOf(v)) 79 reflectType := reflectValue.Type() 80 var fieldAll = map[string]string{} 81 var isExc = false 82 var incNil, incZero bool 83 var alias string 84 if len(filter) > 0 { 85 filter = strings.TrimSpace(filter) 86 parts := strings.SplitN(filter, ".", 2) 87 if len(parts) > 1 { 88 alias = parts[0] + "." 89 filter = parts[1] 90 } 91 parts = strings.SplitN(filter, "#", 2) 92 isExc = strings.HasPrefix(parts[0], "^") 93 if len(parts[0]) > 0 { 94 for _, fieldItem := range strings.Split(strings.TrimPrefix(parts[0], "^"), ",") { 95 fieldParts := strings.SplitN(strings.Trim(strings.TrimSpace(fieldItem), ")"), "(", 2) 96 if len(fieldParts) > 1 { 97 fieldAll[fieldParts[1]] = fieldParts[0] 98 } else { 99 fieldAll[fieldParts[0]] = "" 100 } 101 } 102 } 103 if len(parts) > 1 && len(parts[1]) > 0 { 104 incNil = strings.Contains(","+parts[1]+",", ",nil,") || strings.Contains(","+parts[1]+",", ",all,") 105 incZero = strings.Contains(","+parts[1]+",", ",zero,") || strings.Contains(","+parts[1]+",", ",all,") 106 } 107 } 108 numField := reflectType.NumField() 109 for i := 0; i < numField; i++ { 110 fieldValue := reflectValue.Field(i) 111 fieldType := reflectType.Field(i) 112 fieldName := strings.SplitN(fieldType.Tag.Get(s.Tag), ",", 2)[0] 113 fieldFilter := strings.TrimSpace(strings.TrimPrefix(fieldType.Tag.Get("filter"), "#")) 114 fieldIncNil, fieldIncZero, fieldInline := incNil, incZero, false 115 if len(fieldFilter) > 0 { 116 fieldIncNil = strings.Contains(","+fieldFilter+",", ",nil,") || strings.Contains(","+fieldFilter+",", ",all,") 117 fieldIncZero = strings.Contains(","+fieldFilter+",", ",zero,") || strings.Contains(","+fieldFilter+",", ",all,") 118 fieldInline = strings.Contains(","+fieldFilter+",", ",inline,") 119 } 120 if fieldInline { 121 s.FilterFieldCall(on, fieldValue.Addr().Interface(), filter, call) 122 continue 123 } 124 if len(fieldName) < 1 || fieldName == "-" { 125 continue 126 } 127 if _, ok := fieldAll[fieldName]; (isExc && ok) || (!isExc && len(fieldAll) > 0 && !ok) { 128 continue 129 } 130 131 if !s.CheckValue(fieldValue, fieldIncNil, fieldIncZero) { 132 continue 133 } 134 fieldName = s.NameConv(on, fieldName, fieldType) 135 call(alias+fieldName, fieldAll[fieldName], fieldType, fieldValue.Addr().Interface()) 136 } 137 }