277 lines
7.3 KiB
Go
277 lines
7.3 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 (
|
|
"container/list"
|
|
"encoding/base64"
|
|
"github.com/pborman/uuid"
|
|
"github.com/shopspring/decimal"
|
|
"math"
|
|
"math/big"
|
|
"strconv"
|
|
"time"
|
|
)
|
|
|
|
// DecodeKeyword decodes ~: style keywords.
|
|
func DecodeKeyword(d Decoder, x interface{}) (interface{}, error) {
|
|
s := x.(string)
|
|
var result = Keyword(s)
|
|
return result, nil
|
|
}
|
|
|
|
// DecodeKeyword decodes ~$ style symbols.
|
|
func DecodeSymbol(d Decoder, x interface{}) (interface{}, error) {
|
|
s := x.(string)
|
|
var result = Symbol(s)
|
|
return result, nil
|
|
}
|
|
|
|
// DecodeIdentity returns the value unchanged.
|
|
func DecodeIdentity(d Decoder, x interface{}) (interface{}, error) {
|
|
return x, nil
|
|
}
|
|
|
|
// DecodeCMap decodes maps with composite keys.
|
|
func DecodeCMap(d Decoder, x interface{}) (interface{}, error) {
|
|
|
|
tagged := x.(TaggedValue)
|
|
|
|
if !IsGenericArray(tagged.Value) {
|
|
return nil, NewTransitError("Cmap contents are not an array.", tagged)
|
|
}
|
|
|
|
array := tagged.Value.([]interface{})
|
|
|
|
if (len(array) % 2) != 0 {
|
|
return nil, NewTransitError("Cmap contents must contain an even number of elements.", tagged)
|
|
}
|
|
|
|
var result = NewCMap()
|
|
|
|
l := len(array)
|
|
|
|
for i := 0; i < l; i += 2 {
|
|
key := array[i]
|
|
value := array[i+1]
|
|
result.Append(key, value)
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
// DecodeSet decodes a transit set into a transit.Set instance.
|
|
func DecodeSet(d Decoder, x interface{}) (interface{}, error) {
|
|
tagged := x.(TaggedValue)
|
|
if !IsGenericArray(tagged.Value) {
|
|
return nil, NewTransitError("Set contents are not an array.", tagged)
|
|
}
|
|
values := (tagged.Value).([]interface{})
|
|
result := NewSet(values)
|
|
return result, nil
|
|
}
|
|
|
|
// DecodeList decodes a transit list into a Go list.
|
|
func DecodeList(d Decoder, x interface{}) (interface{}, error) {
|
|
tagged := x.(TaggedValue)
|
|
if !IsGenericArray(tagged.Value) {
|
|
return nil, NewTransitError("List contents are not an array.", tagged)
|
|
}
|
|
values := (tagged.Value).([]interface{})
|
|
result := list.New()
|
|
for _, item := range values {
|
|
result.PushBack(item)
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
// DecodeQuote decodes a transit quoted value by simply returning the value.
|
|
func DecodeQuote(d Decoder, x interface{}) (interface{}, error) {
|
|
tagged := x.(TaggedValue)
|
|
return tagged.Value, nil
|
|
}
|
|
|
|
// DecodeRFC3339 decodes a time value into a Go time instance.
|
|
// TBD not 100% this covers all possible values.
|
|
func DecodeRFC3339(d Decoder, x interface{}) (interface{}, error) {
|
|
s := x.(string)
|
|
var result, err = time.Parse(time.RFC3339Nano, s)
|
|
return result, err
|
|
}
|
|
|
|
// DecodeTime decodes a time value represended as millis since 1970.
|
|
func DecodeTime(d Decoder, x interface{}) (interface{}, error) {
|
|
s := x.(string)
|
|
var millis, _ = strconv.ParseInt(s, 10, 64)
|
|
seconds := millis / 1000
|
|
remainder_millis := millis - (seconds * 1000)
|
|
nanos := remainder_millis * 1000000
|
|
result := time.Unix(seconds, nanos).UTC()
|
|
return result, nil
|
|
}
|
|
|
|
// DecodeBoolean decodes a transit boolean into a Go bool.
|
|
func DecodeBoolean(d Decoder, x interface{}) (interface{}, error) {
|
|
s := x.(string)
|
|
if s == "t" {
|
|
return true, nil
|
|
} else if s == "f" {
|
|
return false, nil
|
|
} else {
|
|
return nil, &TransitError{Message: "Unknown boolean value."}
|
|
}
|
|
}
|
|
|
|
// DecodeBigInteger decodes a transit big integer into a Go big.Int.
|
|
func DecodeBigInteger(d Decoder, x interface{}) (interface{}, error) {
|
|
s := x.(string)
|
|
result := new(big.Int)
|
|
_, good := result.SetString(s, 10)
|
|
if !good {
|
|
return nil, &TransitError{Message: "Unable to part big integer: " + s}
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
// DecodeInteger decodes a transit integer into a plain Go int64
|
|
func DecodeInteger(d Decoder, x interface{}) (interface{}, error) {
|
|
s := x.(string)
|
|
result, err := strconv.ParseInt(s, 10, 64)
|
|
return result, err
|
|
}
|
|
|
|
func newRational(a, b *big.Int) *big.Rat {
|
|
var r = big.NewRat(1, 1)
|
|
r.SetFrac(a, b)
|
|
return r
|
|
}
|
|
|
|
func toBigInt(x interface{}) (*big.Int, error) {
|
|
switch v := x.(type) {
|
|
default:
|
|
return nil, NewTransitError("Not a numeric value", v)
|
|
case *big.Int:
|
|
return v, nil
|
|
case int64:
|
|
return big.NewInt(v), nil
|
|
}
|
|
}
|
|
|
|
// DecodeRatio decodes a transit ratio into a Go big.Rat.
|
|
func DecodeRatio(d Decoder, x interface{}) (interface{}, error) {
|
|
tagged := x.(TaggedValue)
|
|
if !IsGenericArray(tagged.Value) {
|
|
return nil, NewTransitError("Ratio contents are not an array.", tagged)
|
|
}
|
|
|
|
values := (tagged.Value).([]interface{})
|
|
|
|
if len(values) != 2 {
|
|
return nil, NewTransitError("Ratio contents does not contain 2 elements.", tagged)
|
|
}
|
|
|
|
a, err := toBigInt(values[0])
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
b, err := toBigInt(values[1])
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
result := newRational(a, b)
|
|
return *result, nil
|
|
}
|
|
|
|
// DecodeRune decodes a transit char.
|
|
func DecodeRune(d Decoder, x interface{}) (interface{}, error) {
|
|
s := x.(string)
|
|
return rune(s[0]), nil
|
|
}
|
|
|
|
// DecodeFloat decodes the value into a float.
|
|
func DecodeFloat(d Decoder, x interface{}) (interface{}, error) {
|
|
s := x.(string)
|
|
return strconv.ParseFloat(s, 64)
|
|
}
|
|
|
|
// DecodeDecimal decodes a transit big decimal into decimal.Decimal.
|
|
func DecodeDecimal(d Decoder, x interface{}) (interface{}, error) {
|
|
s := x.(string)
|
|
return decimal.NewFromString((s))
|
|
}
|
|
|
|
// DecodeRatio decodes a transit null/nil.
|
|
func DecodeNil(d Decoder, x interface{}) (interface{}, error) {
|
|
return nil, nil
|
|
}
|
|
|
|
// DecodeRatio decodes a transit base64 encoded byte array into a
|
|
// Go byte array.
|
|
func DecodeByte(d Decoder, x interface{}) (interface{}, error) {
|
|
s := x.(string)
|
|
return base64.StdEncoding.DecodeString(s)
|
|
}
|
|
|
|
// DecodeLink decodes a transit link into an instance of Link.
|
|
func DecodeLink(d Decoder, x interface{}) (interface{}, error) {
|
|
tv := x.(TaggedValue)
|
|
v := tv.Value.(map[interface{}]interface{})
|
|
l := NewLink()
|
|
l.Href = v["href"].(*TUri)
|
|
l.Name = v["name"].(string)
|
|
l.Rel = v["rel"].(string)
|
|
l.Prompt = v["prompt"].(string)
|
|
l.Render = v["render"].(string)
|
|
return l, nil
|
|
}
|
|
|
|
// DecodeURI decodes a transit URI into an instance of TUri.
|
|
func DecodeURI(d Decoder, x interface{}) (interface{}, error) {
|
|
s := x.(string)
|
|
return NewTUri(s), nil
|
|
}
|
|
|
|
// DecodeUUID decodes a transit UUID into an instance of net/UUID
|
|
func DecodeUUID(d Decoder, x interface{}) (interface{}, error) {
|
|
s := x.(string)
|
|
var u = uuid.Parse(s)
|
|
if u == nil {
|
|
return nil, &TransitError{Message: "Unable to parse uuid [" + s + "]"}
|
|
}
|
|
return u, nil
|
|
}
|
|
|
|
// DecodeSpecialNumber decodes NaN, INF and -INF into their Go equivalents.
|
|
func DecodeSpecialNumber(d Decoder, x interface{}) (interface{}, error) {
|
|
tag := x.(string)
|
|
if tag == "NaN" {
|
|
return math.NaN(), nil
|
|
} else if tag == "INF" {
|
|
return math.Inf(1), nil
|
|
} else if tag == "-INF" {
|
|
return math.Inf(-1), nil
|
|
} else {
|
|
return nil, &TransitError{Message: "Bad special number:"}
|
|
}
|
|
}
|