status-go/vendor/github.com/russolsen/transit/encode.go

213 lines
6.1 KiB
Go

// Copyright 2016 Russ Olsen. All Rights Reserved.
//
// This code is a Go port of the Java version created and maintained by Cognitect, therefore:
//
// Copyright 2014 Cognitect. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package transit
import (
"bytes"
"container/list"
"github.com/pborman/uuid"
"github.com/shopspring/decimal"
"io"
"math/big"
"net/url"
"reflect"
"time"
)
type Encoder struct {
emitter DataEmitter
valueEncoders map[interface{}]ValueEncoder
}
var goListType = reflect.TypeOf(list.New())
var keywordType = reflect.TypeOf(Keyword(""))
var symbolType = reflect.TypeOf(Symbol(""))
var cmapType = reflect.TypeOf(NewCMap())
var aUrl, _ = url.Parse("http://foo.com")
var urlType = reflect.TypeOf(aUrl)
var turiType = reflect.TypeOf(NewTUri("http://example.com"))
var setType = reflect.TypeOf(Set{})
var timeType = reflect.TypeOf(time.Now())
var bigRatType = reflect.TypeOf(*big.NewRat(int64(1), int64(2)))
var bigIntType = reflect.TypeOf(*big.NewInt(int64(1)))
var bigFloatType = reflect.TypeOf(*big.NewFloat(float64(1.)))
var decimalType = reflect.TypeOf(decimal.NewFromFloat(3))
var uuidType = reflect.TypeOf(uuid.NewRandom())
var linkType = reflect.TypeOf(*NewLink())
var taggedValueType = reflect.TypeOf(TaggedValue{TagId("#foo"), 1})
var runeType = reflect.TypeOf('x')
var nilValue = reflect.ValueOf(nil)
var nilEncoder = NewNilEncoder()
// NewEncoder creates a new encoder set to writ to the stream supplied.
// The verbose parameter controls transit's verbose vs non-verbose mode.
// Generally for production you want verbose = false.
func NewEncoder(w io.Writer, verbose bool) *Encoder {
valueEncoders := make(map[interface{}]ValueEncoder)
var cache Cache
if verbose {
cache = NewNoopCache()
} else {
cache = NewRollingCache()
}
emitter := NewJsonEmitter(w, cache)
e := Encoder{emitter: emitter, valueEncoders: valueEncoders}
e.addHandler(reflect.String, NewStringEncoder())
e.addHandler(reflect.Bool, NewBoolEncoder())
e.addHandler(reflect.Ptr, NewPointerEncoder())
floatEncoder := NewFloatEncoder()
e.addHandler(reflect.Float32, floatEncoder)
e.addHandler(reflect.Float64, floatEncoder)
decimalEncoder := NewDecimalEncoder()
e.addHandler(decimalType, decimalEncoder)
intEncoder := NewIntEncoder()
e.addHandler(reflect.Int, intEncoder)
e.addHandler(reflect.Int8, intEncoder)
e.addHandler(reflect.Int16, intEncoder)
e.addHandler(reflect.Int32, intEncoder)
e.addHandler(reflect.Int64, intEncoder)
uintEncoder := NewUintEncoder()
e.addHandler(reflect.Uint, uintEncoder)
e.addHandler(reflect.Uint8, uintEncoder)
e.addHandler(reflect.Uint16, uintEncoder)
e.addHandler(reflect.Uint32, uintEncoder)
e.addHandler(reflect.Uint64, uintEncoder)
arrayEncoder := NewArrayEncoder()
e.addHandler(reflect.Array, arrayEncoder)
e.addHandler(reflect.Slice, arrayEncoder)
e.addHandler(reflect.Map, NewMapEncoder(verbose))
e.addHandler(runeType, NewRuneEncoder())
e.addHandler(timeType, NewTimeEncoder())
e.addHandler(uuidType, NewUuidEncoder())
e.addHandler(bigIntType, NewBigIntEncoder())
e.addHandler(bigRatType, NewBigRatEncoder())
e.addHandler(bigFloatType, NewBigFloatEncoder())
e.addHandler(goListType, NewListEncoder())
e.addHandler(symbolType, NewSymbolEncoder())
e.addHandler(keywordType, NewKeywordEncoder())
e.addHandler(cmapType, NewCMapEncoder())
e.addHandler(setType, NewSetEncoder())
e.addHandler(urlType, NewUrlEncoder())
e.addHandler(turiType, NewTUriEncoder())
e.addHandler(linkType, NewLinkEncoder())
e.addHandler(taggedValueType, NewTaggedValueEncoder())
return &e
}
// AddHandler adds a new handler to the table used by this encoder
// for encoding values. The t value should be an instance
// of reflect.Type and the c value should be an encoder for that type.
func (e Encoder) AddHandler(t reflect.Type, c ValueEncoder) {
e.addHandler(t, c)
}
// addHandler adds a new handler to the table, but the untyped first
// parameter lets you enter either reflect.Type or reflect.Kind values.
// Used internally.
func (e Encoder) addHandler(t interface{}, c ValueEncoder) {
e.valueEncoders[t] = c
}
// ValueEncoderFor finds the encoder for the given value.
func (e Encoder) ValueEncoderFor(v reflect.Value) ValueEncoder {
// Nil is a special case since it doesn't really work
// very well with the reflect package.
if v == nilValue {
return nilEncoder
}
// Look for an encoder by the specific type.
typeEncoder := e.valueEncoders[v.Type()]
if typeEncoder != nil {
return typeEncoder
}
// If we can't find a type encoder, try finding one
// by type. This is will catch values of know kinds,
// say int64 or string which have a different specific
// type.
kindEncoder := e.valueEncoders[v.Kind()]
if kindEncoder != nil {
return kindEncoder
}
// No encoder, for this type, return the error encoder.
return NewErrorEncoder()
}
// Given a Value, encode it.
func (e Encoder) EncodeValue(v reflect.Value, asKey bool) error {
valueEncoder := e.ValueEncoderFor(v)
return valueEncoder.Encode(e, v, asKey)
}
// Given a raw interface, encode it.
func (e Encoder) EncodeInterface(x interface{}, asKey bool) error {
v := reflect.ValueOf(x)
return e.EncodeValue(v, asKey)
}
// Encode a value at the top level.
func (e Encoder) Encode(x interface{}) error {
v := reflect.ValueOf(x)
valueEncoder := e.ValueEncoderFor(v)
if valueEncoder.IsStringable(v) {
x = TaggedValue{TagId("'"), x}
}
return e.EncodeInterface(x, false)
}
// Encode the given value to a string.
func EncodeToString(x interface{}, verbose bool) (string, error) {
var buf bytes.Buffer
var encoder = NewEncoder(&buf, verbose)
err := encoder.Encode(x)
if err != nil {
return "", err
}
return buf.String(), nil
}