Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
155 lines (135 sloc) 3.75 KB
// Copyright 2019-present Facebook Inc. All rights reserved.
// This source code is licensed under the Apache 2.0 license found
// in the LICENSE file in the root directory of this source tree.
package graphson
import (
"reflect"
"strings"
"unsafe"
"github.com/json-iterator/go"
"github.com/modern-go/reflect2"
"github.com/pkg/errors"
)
// A Type is a graphson type.
type Type string
// graphson typed value types.
const (
// core
doubleType Type = "g:Double"
floatType Type = "g:Float"
int32Type Type = "g:Int32"
int64Type Type = "g:Int64"
listType Type = "g:List"
mapType Type = "g:Map"
Timestamp Type = "g:Timestamp"
Date Type = "g:Date"
// extended
bigIntegerType Type = "gx:BigInteger"
bigDecimal Type = "gx:BigDecimal"
byteType Type = "gx:Byte"
byteBufferType Type = "gx:ByteBuffer"
int16Type Type = "gx:Int16"
)
// String implements fmt.Stringer interface.
func (typ Type) String() string {
return string(typ)
}
// CheckType implements typeChecker interface.
func (typ Type) CheckType(other Type) error {
if typ != other {
return errors.Errorf("expect type %s, but found %s", typ, other)
}
return nil
}
// Types is a slice of Type.
type Types []Type
// Contains reports whether a slice of types contains a particular type.
func (types Types) Contains(typ Type) bool {
for i := range types {
if types[i] == typ {
return true
}
}
return false
}
// String implements fmt.Stringer interface.
func (types Types) String() string {
var builder strings.Builder
builder.WriteByte('[')
for i := range types {
if i > 0 {
builder.WriteByte(',')
}
builder.WriteString(types[i].String())
}
builder.WriteByte(']')
return builder.String()
}
// CheckType implements typeChecker interface.
func (types Types) CheckType(typ Type) error {
if !types.Contains(typ) {
return errors.Errorf("expect any of %s, but found %s", types, typ)
}
return nil
}
// Typer is the interface implemented by types that
// define an underlying graphson type.
type Typer interface {
GraphsonType() Type
}
var typerType = reflect2.TypeOfPtr((*Typer)(nil)).Elem()
// DecoratorOfTyper decorates a value encoder of a Typer interface.
func (ext encodeExtension) DecoratorOfTyper(typ reflect2.Type, enc jsoniter.ValEncoder) jsoniter.ValEncoder {
if typ.Kind() != reflect.Struct {
return nil
}
if typ.Implements(typerType) {
return typerEncoder{
typeEncoder: typeEncoder{ValEncoder: enc},
typerOf: func(ptr unsafe.Pointer) Typer {
return typ.UnsafeIndirect(ptr).(Typer)
},
}
}
ptrType := reflect2.PtrTo(typ)
if ptrType.Implements(typerType) {
return typerEncoder{
typeEncoder: typeEncoder{ValEncoder: enc},
typerOf: func(ptr unsafe.Pointer) Typer {
// nolint: gas
return ptrType.UnsafeIndirect(unsafe.Pointer(&ptr)).(Typer)
},
}
}
return nil
}
// DecoratorOfTyper decorates a value decoder of a Typer interface.
func (ext decodeExtension) DecoratorOfTyper(typ reflect2.Type, dec jsoniter.ValDecoder) jsoniter.ValDecoder {
ptrType := reflect2.PtrTo(typ)
if ptrType.Implements(typerType) {
return typerDecoder{
typeDecoder: typeDecoder{ValDecoder: dec},
typerOf: func(ptr unsafe.Pointer) Typer {
// nolint: gas
return ptrType.UnsafeIndirect(unsafe.Pointer(&ptr)).(Typer)
},
}
}
return nil
}
type typerEncoder struct {
typeEncoder
typerOf func(unsafe.Pointer) Typer
}
func (enc typerEncoder) Encode(ptr unsafe.Pointer, stream *jsoniter.Stream) {
enc.typeEncoder.Type = enc.typerOf(ptr).GraphsonType()
enc.typeEncoder.Encode(ptr, stream)
}
type typerDecoder struct {
typeDecoder
typerOf func(unsafe.Pointer) Typer
}
func (dec typerDecoder) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) {
dec.typeDecoder.typeChecker = dec.typerOf(ptr).GraphsonType()
dec.typeDecoder.Decode(ptr, iter)
}
You can’t perform that action at this time.