github.com/kunlun-qilian/sqlx/v3@v3.0.0/builder/utils_field.go (about)

     1  package builder
     2  
     3  import (
     4  	"context"
     5  	"database/sql/driver"
     6  	"fmt"
     7  	"go/ast"
     8  	"reflect"
     9  	"strings"
    10  	"sync"
    11  
    12  	reflectx "github.com/go-courier/x/reflect"
    13  	typesx "github.com/go-courier/x/types"
    14  )
    15  
    16  func StructFieldsFor(ctx context.Context, typ typesx.Type) []*StructField {
    17  	return defaultStructFieldsFactory.TableFieldsFor(ctx, typ)
    18  }
    19  
    20  var defaultStructFieldsFactory = &StructFieldsFactory{}
    21  
    22  type StructFieldsFactory struct {
    23  	cache sync.Map
    24  }
    25  
    26  func (tf *StructFieldsFactory) TableFieldsFor(ctx context.Context, typ typesx.Type) []*StructField {
    27  	typ = typesx.Deref(typ)
    28  
    29  	underlying := typ.Unwrap()
    30  
    31  	if v, ok := tf.cache.Load(underlying); ok {
    32  		return v.([]*StructField)
    33  	}
    34  
    35  	tfs := make([]*StructField, 0)
    36  
    37  	EachStructField(ctx, typ, func(f *StructField) bool {
    38  		tagDB := f.Tags["db"]
    39  		if tagDB != "" && tagDB != "-" {
    40  			tfs = append(tfs, f)
    41  		}
    42  		return true
    43  	})
    44  
    45  	tf.cache.Store(underlying, tfs)
    46  
    47  	return tfs
    48  }
    49  
    50  var typeModel = reflect.TypeOf((*Model)(nil)).Elem()
    51  var driverValuer = reflect.TypeOf((*driver.Valuer)(nil)).Elem()
    52  
    53  func EachStructField(ctx context.Context, tpe typesx.Type, each func(p *StructField) bool) {
    54  	if tpe.Kind() != reflect.Struct {
    55  		panic(fmt.Errorf("model %s must be a struct", tpe.Name()))
    56  	}
    57  
    58  	var walk func(tpe typesx.Type, modelLoc []int, parents ...int)
    59  
    60  	walk = func(tpe typesx.Type, modelLoc []int, parents ...int) {
    61  		if ok := tpe.Implements(typesx.FromRType(typeModel)); ok {
    62  			modelLoc = parents
    63  		}
    64  
    65  		for i := 0; i < tpe.NumField(); i++ {
    66  			f := tpe.Field(i)
    67  
    68  			if !ast.IsExported(f.Name()) {
    69  				continue
    70  			}
    71  
    72  			loc := append(parents, i)
    73  
    74  			tags := reflectx.ParseStructTags(string(f.Tag()))
    75  
    76  			displayName := f.Name()
    77  
    78  			tagDB, hasDB := tags["db"]
    79  			if hasDB {
    80  				if name := tagDB.Name(); name == "-" {
    81  					// skip name:"-"
    82  					continue
    83  				} else {
    84  					if name != "" {
    85  						displayName = name
    86  					}
    87  				}
    88  			}
    89  
    90  			if (f.Anonymous() || f.Type().Name() == f.Name()) && (!hasDB) {
    91  				fieldType := f.Type()
    92  
    93  				if !fieldType.Implements(typesx.FromRType(driverValuer)) {
    94  					for fieldType.Kind() == reflect.Ptr {
    95  						fieldType = fieldType.Elem()
    96  					}
    97  
    98  					if fieldType.Kind() == reflect.Struct {
    99  						walk(fieldType, modelLoc, loc...)
   100  						continue
   101  					}
   102  				}
   103  			}
   104  
   105  			p := &StructField{}
   106  			p.FieldName = f.Name()
   107  			p.Type = f.Type()
   108  			p.Field = f
   109  			p.Tags = tags
   110  			p.Name = strings.ToLower(displayName)
   111  
   112  			p.Loc = make([]int, len(loc))
   113  			copy(p.Loc, loc)
   114  
   115  			p.ModelLoc = make([]int, len(modelLoc))
   116  			copy(p.ModelLoc, modelLoc)
   117  
   118  			p.ColumnType = *ColumnTypeFromTypeAndTag(p.Type, string(tagDB))
   119  
   120  			if !each(p) {
   121  				break
   122  			}
   123  		}
   124  	}
   125  
   126  	walk(tpe, []int{})
   127  }
   128  
   129  type StructField struct {
   130  	Name       string
   131  	FieldName  string
   132  	Type       typesx.Type
   133  	Field      typesx.StructField
   134  	Tags       map[string]reflectx.StructTag
   135  	Loc        []int
   136  	ModelLoc   []int
   137  	ColumnType ColumnType
   138  }
   139  
   140  func fieldValue(structReflectValue reflect.Value, locs []int) reflect.Value {
   141  	n := len(locs)
   142  
   143  	if n == 0 {
   144  		return structReflectValue
   145  	}
   146  
   147  	if n < 0 {
   148  		return reflect.Value{}
   149  	}
   150  
   151  	structReflectValue = reflectx.Indirect(structReflectValue)
   152  
   153  	fv := structReflectValue
   154  
   155  	for i := 0; i < n; i++ {
   156  		loc := locs[i]
   157  		fv = fv.Field(loc)
   158  
   159  		// last loc should keep ptr value
   160  		if i < n-1 {
   161  			for fv.Kind() == reflect.Ptr {
   162  				// notice the ptr struct ensure only for Ptr Anonymous StructField
   163  				if fv.IsNil() {
   164  					fv.Set(reflectx.New(fv.Type()))
   165  				}
   166  				fv = fv.Elem()
   167  			}
   168  		}
   169  	}
   170  
   171  	return fv
   172  }
   173  
   174  func (p *StructField) FieldValue(structReflectValue reflect.Value) reflect.Value {
   175  	return fieldValue(structReflectValue, p.Loc)
   176  }
   177  
   178  func (p *StructField) FieldModelValue(structReflectValue reflect.Value) reflect.Value {
   179  	return fieldValue(structReflectValue, p.ModelLoc)
   180  }