-
Notifications
You must be signed in to change notification settings - Fork 8
/
value_decoder.go
276 lines (235 loc) · 7.27 KB
/
value_decoder.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
// 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:"}
}
}