Add Serializer Interface
This commit is contained in:
		
							parent
							
								
									1b6cc25e19
								
							
						
					
					
						commit
						3c77eb0bb0
					
				@ -15,12 +15,17 @@ import (
 | 
			
		||||
	"gorm.io/gorm/utils"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type DataType string
 | 
			
		||||
 | 
			
		||||
type TimeType int64
 | 
			
		||||
type (
 | 
			
		||||
	// DataType GORM data type
 | 
			
		||||
	DataType string
 | 
			
		||||
	// TimeType GORM time type
 | 
			
		||||
	TimeType int64
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// TimeReflectType time's reflect type
 | 
			
		||||
var TimeReflectType = reflect.TypeOf(time.Time{})
 | 
			
		||||
 | 
			
		||||
// GORM time types
 | 
			
		||||
const (
 | 
			
		||||
	UnixTime        TimeType = 1
 | 
			
		||||
	UnixSecond      TimeType = 2
 | 
			
		||||
@ -28,6 +33,7 @@ const (
 | 
			
		||||
	UnixNanosecond  TimeType = 4
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// GORM fields types
 | 
			
		||||
const (
 | 
			
		||||
	Bool   DataType = "bool"
 | 
			
		||||
	Int    DataType = "int"
 | 
			
		||||
@ -38,6 +44,7 @@ const (
 | 
			
		||||
	Bytes  DataType = "bytes"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Field is the representation of model schema's field
 | 
			
		||||
type Field struct {
 | 
			
		||||
	Name                   string
 | 
			
		||||
	DBName                 string
 | 
			
		||||
@ -50,9 +57,9 @@ type Field struct {
 | 
			
		||||
	Creatable              bool
 | 
			
		||||
	Updatable              bool
 | 
			
		||||
	Readable               bool
 | 
			
		||||
	HasDefaultValue        bool
 | 
			
		||||
	AutoCreateTime         TimeType
 | 
			
		||||
	AutoUpdateTime         TimeType
 | 
			
		||||
	HasDefaultValue        bool
 | 
			
		||||
	DefaultValue           string
 | 
			
		||||
	DefaultValueInterface  interface{}
 | 
			
		||||
	NotNull                bool
 | 
			
		||||
@ -61,6 +68,7 @@ type Field struct {
 | 
			
		||||
	Size                   int
 | 
			
		||||
	Precision              int
 | 
			
		||||
	Scale                  int
 | 
			
		||||
	IgnoreMigration        bool
 | 
			
		||||
	FieldType              reflect.Type
 | 
			
		||||
	IndirectFieldType      reflect.Type
 | 
			
		||||
	StructField            reflect.StructField
 | 
			
		||||
@ -72,24 +80,32 @@ type Field struct {
 | 
			
		||||
	ReflectValueOf         func(context.Context, reflect.Value) reflect.Value
 | 
			
		||||
	ValueOf                func(context.Context, reflect.Value) (value interface{}, zero bool)
 | 
			
		||||
	Set                    func(context.Context, reflect.Value, interface{}) error
 | 
			
		||||
	IgnoreMigration        bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ParseField parses reflect.StructField to Field
 | 
			
		||||
func (schema *Schema) ParseField(fieldStruct reflect.StructField) *Field {
 | 
			
		||||
	var err error
 | 
			
		||||
	var (
 | 
			
		||||
		err        error
 | 
			
		||||
		tagSetting = ParseTagSetting(fieldStruct.Tag.Get("gorm"), ";")
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	field := &Field{
 | 
			
		||||
		Name:                   fieldStruct.Name,
 | 
			
		||||
		DBName:                 tagSetting["COLUMN"],
 | 
			
		||||
		BindNames:              []string{fieldStruct.Name},
 | 
			
		||||
		FieldType:              fieldStruct.Type,
 | 
			
		||||
		IndirectFieldType:      fieldStruct.Type,
 | 
			
		||||
		StructField:            fieldStruct,
 | 
			
		||||
		Tag:                    fieldStruct.Tag,
 | 
			
		||||
		TagSettings:            tagSetting,
 | 
			
		||||
		Schema:                 schema,
 | 
			
		||||
		Creatable:              true,
 | 
			
		||||
		Updatable:              true,
 | 
			
		||||
		Readable:               true,
 | 
			
		||||
		Tag:                    fieldStruct.Tag,
 | 
			
		||||
		TagSettings:            ParseTagSetting(fieldStruct.Tag.Get("gorm"), ";"),
 | 
			
		||||
		Schema:                 schema,
 | 
			
		||||
		PrimaryKey:             utils.CheckTruth(tagSetting["PRIMARYKEY"], tagSetting["PRIMARY_KEY"]),
 | 
			
		||||
		NotNull:                utils.CheckTruth(tagSetting["NOT NULL"], tagSetting["NOTNULL"]),
 | 
			
		||||
		Unique:                 utils.CheckTruth(tagSetting["UNIQUE"]),
 | 
			
		||||
		Comment:                tagSetting["COMMENT"],
 | 
			
		||||
		AutoIncrementIncrement: 1,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -139,16 +155,6 @@ func (schema *Schema) ParseField(fieldStruct reflect.StructField) *Field {
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if dbName, ok := field.TagSettings["COLUMN"]; ok {
 | 
			
		||||
		field.DBName = dbName
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if val, ok := field.TagSettings["PRIMARYKEY"]; ok && utils.CheckTruth(val) {
 | 
			
		||||
		field.PrimaryKey = true
 | 
			
		||||
	} else if val, ok := field.TagSettings["PRIMARY_KEY"]; ok && utils.CheckTruth(val) {
 | 
			
		||||
		field.PrimaryKey = true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if val, ok := field.TagSettings["AUTOINCREMENT"]; ok && utils.CheckTruth(val) {
 | 
			
		||||
		field.AutoIncrement = true
 | 
			
		||||
		field.HasDefaultValue = true
 | 
			
		||||
@ -177,20 +183,6 @@ func (schema *Schema) ParseField(fieldStruct reflect.StructField) *Field {
 | 
			
		||||
		field.Scale, _ = strconv.Atoi(s)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if val, ok := field.TagSettings["NOT NULL"]; ok && utils.CheckTruth(val) {
 | 
			
		||||
		field.NotNull = true
 | 
			
		||||
	} else if val, ok := field.TagSettings["NOTNULL"]; ok && utils.CheckTruth(val) {
 | 
			
		||||
		field.NotNull = true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if val, ok := field.TagSettings["UNIQUE"]; ok && utils.CheckTruth(val) {
 | 
			
		||||
		field.Unique = true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if val, ok := field.TagSettings["COMMENT"]; ok {
 | 
			
		||||
		field.Comment = val
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// default value is function or null or blank (primary keys)
 | 
			
		||||
	field.DefaultValue = strings.TrimSpace(field.DefaultValue)
 | 
			
		||||
	skipParseDefaultValue := strings.Contains(field.DefaultValue, "(") &&
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,7 @@
 | 
			
		||||
package schema_test
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"database/sql"
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"sync"
 | 
			
		||||
@ -57,7 +58,7 @@ func TestFieldValuerAndSetter(t *testing.T) {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for k, v := range newValues {
 | 
			
		||||
		if err := userSchema.FieldsByDBName[k].Set(reflectValue, v); err != nil {
 | 
			
		||||
		if err := userSchema.FieldsByDBName[k].Set(context.Background(), reflectValue, v); err != nil {
 | 
			
		||||
			t.Errorf("no error should happen when assign value to field %v, but got %v", k, err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@ -80,7 +81,7 @@ func TestFieldValuerAndSetter(t *testing.T) {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for k, v := range newValues2 {
 | 
			
		||||
		if err := userSchema.FieldsByDBName[k].Set(reflectValue, v); err != nil {
 | 
			
		||||
		if err := userSchema.FieldsByDBName[k].Set(context.Background(), reflectValue, v); err != nil {
 | 
			
		||||
			t.Errorf("no error should happen when assign value to field %v, but got %v", k, err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@ -132,7 +133,7 @@ func TestPointerFieldValuerAndSetter(t *testing.T) {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for k, v := range newValues {
 | 
			
		||||
		if err := userSchema.FieldsByDBName[k].Set(reflectValue, v); err != nil {
 | 
			
		||||
		if err := userSchema.FieldsByDBName[k].Set(context.Background(), reflectValue, v); err != nil {
 | 
			
		||||
			t.Errorf("no error should happen when assign value to field %v, but got %v", k, err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@ -151,7 +152,7 @@ func TestPointerFieldValuerAndSetter(t *testing.T) {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for k, v := range newValues2 {
 | 
			
		||||
		if err := userSchema.FieldsByDBName[k].Set(reflectValue, v); err != nil {
 | 
			
		||||
		if err := userSchema.FieldsByDBName[k].Set(context.Background(), reflectValue, v); err != nil {
 | 
			
		||||
			t.Errorf("no error should happen when assign value to field %v, but got %v", k, err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@ -202,7 +203,7 @@ func TestAdvancedDataTypeValuerAndSetter(t *testing.T) {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for k, v := range newValues {
 | 
			
		||||
		if err := userSchema.FieldsByDBName[k].Set(reflectValue, v); err != nil {
 | 
			
		||||
		if err := userSchema.FieldsByDBName[k].Set(context.Background(), reflectValue, v); err != nil {
 | 
			
		||||
			t.Errorf("no error should happen when assign value to field %v, but got %v", k, err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@ -219,7 +220,7 @@ func TestAdvancedDataTypeValuerAndSetter(t *testing.T) {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for k, v := range newValues2 {
 | 
			
		||||
		if err := userSchema.FieldsByDBName[k].Set(reflectValue, v); err != nil {
 | 
			
		||||
		if err := userSchema.FieldsByDBName[k].Set(context.Background(), reflectValue, v); err != nil {
 | 
			
		||||
			t.Errorf("no error should happen when assign value to field %v, but got %v", k, err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -2,6 +2,7 @@ package schema
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"database/sql/driver"
 | 
			
		||||
	"reflect"
 | 
			
		||||
 | 
			
		||||
	"gorm.io/gorm/clause"
 | 
			
		||||
@ -12,8 +13,26 @@ type GormDataTypeInterface interface {
 | 
			
		||||
	GormDataType() string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Serializer serializer interface
 | 
			
		||||
type Serializer interface {
 | 
			
		||||
// Serializer field value serializer
 | 
			
		||||
type Serializer struct {
 | 
			
		||||
	Field       *Field
 | 
			
		||||
	Interface   SerializerInterface
 | 
			
		||||
	Destination reflect.Value
 | 
			
		||||
	Context     context.Context
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Scan implements sql.Scanner interface
 | 
			
		||||
func (s *Serializer) Scan(value interface{}) error {
 | 
			
		||||
	return s.Interface.Scan(s.Context, s.Field, s.Destination, value)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Value implements driver.Valuer interface
 | 
			
		||||
func (s Serializer) Value() (driver.Value, error) {
 | 
			
		||||
	return s.Interface.Value(s.Context, s.Field, s.Destination)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SerializerInterface serializer interface
 | 
			
		||||
type SerializerInterface interface {
 | 
			
		||||
	Scan(ctx context.Context, field *Field, dst reflect.Value, dbValue interface{}) error
 | 
			
		||||
	Value(ctx context.Context, field *Field, dst reflect.Value) (interface{}, error)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,7 @@
 | 
			
		||||
package schema_test
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"strings"
 | 
			
		||||
@ -203,7 +204,7 @@ func checkSchemaRelation(t *testing.T, s *schema.Schema, relation Relation) {
 | 
			
		||||
func checkField(t *testing.T, s *schema.Schema, value reflect.Value, values map[string]interface{}) {
 | 
			
		||||
	for k, v := range values {
 | 
			
		||||
		t.Run("CheckField/"+k, func(t *testing.T) {
 | 
			
		||||
			fv, _ := s.FieldsByDBName[k].ValueOf(value)
 | 
			
		||||
			fv, _ := s.FieldsByDBName[k].ValueOf(context.Background(), value)
 | 
			
		||||
			tests.AssertEqual(t, v, fv)
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -36,17 +36,14 @@ func IsValidDBNameChar(c rune) bool {
 | 
			
		||||
	return !unicode.IsLetter(c) && !unicode.IsNumber(c) && c != '.' && c != '*' && c != '_' && c != '$' && c != '@'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func CheckTruth(val interface{}) bool {
 | 
			
		||||
	if v, ok := val.(bool); ok {
 | 
			
		||||
		return v
 | 
			
		||||
// CheckTruth check string true or not
 | 
			
		||||
func CheckTruth(vals ...string) bool {
 | 
			
		||||
	for _, val := range vals {
 | 
			
		||||
		if !strings.EqualFold(val, "false") && val != "" {
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if v, ok := val.(string); ok {
 | 
			
		||||
		v = strings.ToLower(v)
 | 
			
		||||
		return v != "false"
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return !reflect.ValueOf(val).IsZero()
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func ToStringKey(values ...interface{}) string {
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user