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  }