package schema
import (
"bytes"
"context"
"database/sql"
"database/sql/driver"
"encoding/gob"
"encoding/json"
"fmt"
"reflect"
"strings"
"sync"
"time"
)
var serializerMap = sync .Map {}
func RegisterSerializer (name string , serializer SerializerInterface ) {
serializerMap .Store (strings .ToLower (name ), serializer )
}
func GetSerializer (name string ) (serializer SerializerInterface , ok bool ) {
v , ok := serializerMap .Load (strings .ToLower (name ))
if ok {
serializer , ok = v .(SerializerInterface )
}
return serializer , ok
}
func init() {
RegisterSerializer ("json" , JSONSerializer {})
RegisterSerializer ("unixtime" , UnixSecondSerializer {})
RegisterSerializer ("gob" , GobSerializer {})
}
type serializer struct {
Field *Field
Serializer SerializerInterface
SerializeValuer SerializerValuerInterface
Destination reflect .Value
Context context .Context
value interface {}
fieldValue interface {}
}
func (s *serializer ) Scan (value interface {}) error {
s .value = value
return nil
}
func (s serializer ) Value () (driver .Value , error ) {
return s .SerializeValuer .Value (s .Context , s .Field , s .Destination , s .fieldValue )
}
type SerializerInterface interface {
Scan (ctx context .Context , field *Field , dst reflect .Value , dbValue interface {}) error
SerializerValuerInterface
}
type SerializerValuerInterface interface {
Value (ctx context .Context , field *Field , dst reflect .Value , fieldValue interface {}) (interface {}, error )
}
type JSONSerializer struct {}
func (JSONSerializer ) Scan (ctx context .Context , field *Field , dst reflect .Value , dbValue interface {}) (err error ) {
fieldValue := reflect .New (field .FieldType )
if dbValue != nil {
var bytes []byte
switch v := dbValue .(type ) {
case []byte :
bytes = v
case string :
bytes = []byte (v )
default :
return fmt .Errorf ("failed to unmarshal JSONB value: %#v" , dbValue )
}
if len (bytes ) > 0 {
err = json .Unmarshal (bytes , fieldValue .Interface ())
}
}
field .ReflectValueOf (ctx , dst ).Set (fieldValue .Elem ())
return
}
func (JSONSerializer ) Value (ctx context .Context , field *Field , dst reflect .Value , fieldValue interface {}) (interface {}, error ) {
result , err := json .Marshal (fieldValue )
if string (result ) == "null" {
if field .TagSettings ["NOT NULL" ] != "" {
return "" , nil
}
return nil , err
}
return string (result ), err
}
type UnixSecondSerializer struct {}
func (UnixSecondSerializer ) Scan (ctx context .Context , field *Field , dst reflect .Value , dbValue interface {}) (err error ) {
t := sql .NullTime {}
if err = t .Scan (dbValue ); err == nil && t .Valid {
err = field .Set (ctx , dst , t .Time .Unix ())
}
return
}
func (UnixSecondSerializer ) Value (ctx context .Context , field *Field , dst reflect .Value , fieldValue interface {}) (result interface {}, err error ) {
rv := reflect .ValueOf (fieldValue )
switch v := fieldValue .(type ) {
case int64 , int , uint , uint64 , int32 , uint32 , int16 , uint16 :
result = time .Unix (reflect .Indirect (rv ).Int (), 0 )
case *int64 , *int , *uint , *uint64 , *int32 , *uint32 , *int16 , *uint16 :
if rv .IsZero () {
return nil , nil
}
result = time .Unix (reflect .Indirect (rv ).Int (), 0 )
default :
err = fmt .Errorf ("invalid field type %#v for UnixSecondSerializer, only int, uint supported" , v )
}
return
}
type GobSerializer struct {}
func (GobSerializer ) Scan (ctx context .Context , field *Field , dst reflect .Value , dbValue interface {}) (err error ) {
fieldValue := reflect .New (field .FieldType )
if dbValue != nil {
var bytesValue []byte
switch v := dbValue .(type ) {
case []byte :
bytesValue = v
default :
return fmt .Errorf ("failed to unmarshal gob value: %#v" , dbValue )
}
if len (bytesValue ) > 0 {
decoder := gob .NewDecoder (bytes .NewBuffer (bytesValue ))
err = decoder .Decode (fieldValue .Interface ())
}
}
field .ReflectValueOf (ctx , dst ).Set (fieldValue .Elem ())
return
}
func (GobSerializer ) Value (ctx context .Context , field *Field , dst reflect .Value , fieldValue interface {}) (interface {}, error ) {
buf := new (bytes .Buffer )
err := gob .NewEncoder (buf ).Encode (fieldValue )
return buf .Bytes (), err
}
The pages are generated with Golds v0.6.7 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @Go100and1 (reachable from the left QR code) to get the latest news of Golds .