// 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:"} } }