github.com/octohelm/storage@v0.0.0-20240516030302-1ac2cc1ea347/pkg/sqlbuilder/utils_field.go (about)

     1  package sqlbuilder
     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/octohelm/x/reflect"
    13  	typesx "github.com/octohelm/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  	(&fieldWalker{}).walk(ctx, tpe, each)
    58  }
    59  
    60  type fieldWalker struct {
    61  	loc       []int
    62  	modelLoc  []int
    63  	modelType typesx.Type
    64  }
    65  
    66  func (w *fieldWalker) walk(ctx context.Context, tpe typesx.Type, each func(p *StructField) bool) {
    67  	modelLoc := w.modelLoc[:]
    68  	modelType := w.modelType
    69  
    70  	if ok := tpe.Implements(typesx.FromRType(typeModel)); ok {
    71  		if modelType != nil && modelType.NumField() == 1 && modelType.Field(0).Anonymous() {
    72  			// extendable
    73  
    74  		} else {
    75  			modelType = tpe
    76  			modelLoc = w.loc[:]
    77  		}
    78  	}
    79  
    80  	for i := 0; i < tpe.NumField(); i++ {
    81  		f := tpe.Field(i)
    82  
    83  		if !ast.IsExported(f.Name()) {
    84  			continue
    85  		}
    86  
    87  		loc := append(w.loc, i)
    88  
    89  		tags := reflectx.ParseStructTags(string(f.Tag()))
    90  		displayName := f.Name()
    91  
    92  		tagDB, hasDB := tags["db"]
    93  		if hasDB {
    94  			if name := tagDB.Name(); name == "-" {
    95  				// skip name:"-"
    96  				continue
    97  			} else {
    98  				if name != "" {
    99  					displayName = name
   100  				}
   101  			}
   102  		}
   103  
   104  		if (f.Anonymous() || f.Type().Name() == f.Name()) && (!hasDB) {
   105  			fieldType := f.Type()
   106  
   107  			if !fieldType.Implements(typesx.FromRType(driverValuer)) {
   108  				for fieldType.Kind() == reflect.Ptr {
   109  					fieldType = fieldType.Elem()
   110  				}
   111  
   112  				if fieldType.Kind() == reflect.Struct {
   113  					(&fieldWalker{
   114  						loc:       loc,
   115  						modelType: modelType,
   116  						modelLoc:  modelLoc,
   117  					}).walk(ctx, fieldType, each)
   118  					continue
   119  				}
   120  			}
   121  		}
   122  
   123  		p := &StructField{}
   124  		p.FieldName = f.Name()
   125  		p.Type = f.Type()
   126  		p.Field = f
   127  		p.Tags = tags
   128  		p.Name = strings.ToLower(displayName)
   129  
   130  		p.Loc = make([]int, len(loc))
   131  		copy(p.Loc, loc)
   132  
   133  		p.ModelLoc = make([]int, len(modelLoc))
   134  		copy(p.ModelLoc, modelLoc)
   135  
   136  		p.ColumnType = *ColumnDefFromTypeAndTag(p.Type, string(tagDB))
   137  
   138  		if !each(p) {
   139  			break
   140  		}
   141  	}
   142  }
   143  
   144  type StructField struct {
   145  	Name       string
   146  	FieldName  string
   147  	Type       typesx.Type
   148  	Field      typesx.StructField
   149  	Tags       map[string]reflectx.StructTag
   150  	Loc        []int
   151  	ModelLoc   []int
   152  	ColumnType ColumnDef
   153  }
   154  
   155  func fieldValue(structReflectValue reflect.Value, locs []int) reflect.Value {
   156  	n := len(locs)
   157  
   158  	if n == 0 {
   159  		return structReflectValue
   160  	}
   161  
   162  	if n < 0 {
   163  		return reflect.Value{}
   164  	}
   165  
   166  	structReflectValue = reflectx.Indirect(structReflectValue)
   167  
   168  	fv := structReflectValue
   169  
   170  	for i := 0; i < n; i++ {
   171  		loc := locs[i]
   172  		fv = fv.Field(loc)
   173  
   174  		// last loc should keep ptr value
   175  		if i < n-1 {
   176  			for fv.Kind() == reflect.Ptr {
   177  				// notice the ptr struct ensure only for Ptr Anonymous StructField
   178  				if fv.IsNil() {
   179  					fv.Set(reflectx.New(fv.Type()))
   180  				}
   181  				fv = fv.Elem()
   182  			}
   183  		}
   184  	}
   185  
   186  	return fv
   187  }
   188  
   189  func (p *StructField) FieldValue(structReflectValue reflect.Value) reflect.Value {
   190  	return fieldValue(structReflectValue, p.Loc)
   191  }
   192  
   193  func (p *StructField) FieldModelValue(structReflectValue reflect.Value) reflect.Value {
   194  	return fieldValue(structReflectValue, p.ModelLoc)
   195  }