1
2
3
4
5
6
7 package bsonrw
8
9 import (
10 "encoding/base64"
11 "errors"
12 "fmt"
13 "math"
14 "strconv"
15 "time"
16
17 "go.mongodb.org/mongo-driver/bson/bsontype"
18 "go.mongodb.org/mongo-driver/bson/primitive"
19 )
20
21 func wrapperKeyBSONType(key string) bsontype.Type {
22 switch key {
23 case "$numberInt":
24 return bsontype.Int32
25 case "$numberLong":
26 return bsontype.Int64
27 case "$oid":
28 return bsontype.ObjectID
29 case "$symbol":
30 return bsontype.Symbol
31 case "$numberDouble":
32 return bsontype.Double
33 case "$numberDecimal":
34 return bsontype.Decimal128
35 case "$binary":
36 return bsontype.Binary
37 case "$code":
38 return bsontype.JavaScript
39 case "$scope":
40 return bsontype.CodeWithScope
41 case "$timestamp":
42 return bsontype.Timestamp
43 case "$regularExpression":
44 return bsontype.Regex
45 case "$dbPointer":
46 return bsontype.DBPointer
47 case "$date":
48 return bsontype.DateTime
49 case "$minKey":
50 return bsontype.MinKey
51 case "$maxKey":
52 return bsontype.MaxKey
53 case "$undefined":
54 return bsontype.Undefined
55 }
56
57 return bsontype.EmbeddedDocument
58 }
59
60 func (ejv *extJSONValue) parseBinary() (b []byte, subType byte, err error) {
61 if ejv.t != bsontype.EmbeddedDocument {
62 return nil, 0, fmt.Errorf("$binary value should be object, but instead is %s", ejv.t)
63 }
64
65 binObj := ejv.v.(*extJSONObject)
66 bFound := false
67 stFound := false
68
69 for i, key := range binObj.keys {
70 val := binObj.values[i]
71
72 switch key {
73 case "base64":
74 if bFound {
75 return nil, 0, errors.New("duplicate base64 key in $binary")
76 }
77
78 if val.t != bsontype.String {
79 return nil, 0, fmt.Errorf("$binary base64 value should be string, but instead is %s", val.t)
80 }
81
82 base64Bytes, err := base64.StdEncoding.DecodeString(val.v.(string))
83 if err != nil {
84 return nil, 0, fmt.Errorf("invalid $binary base64 string: %s", val.v.(string))
85 }
86
87 b = base64Bytes
88 bFound = true
89 case "subType":
90 if stFound {
91 return nil, 0, errors.New("duplicate subType key in $binary")
92 }
93
94 if val.t != bsontype.String {
95 return nil, 0, fmt.Errorf("$binary subType value should be string, but instead is %s", val.t)
96 }
97
98 i, err := strconv.ParseInt(val.v.(string), 16, 64)
99 if err != nil {
100 return nil, 0, fmt.Errorf("invalid $binary subType string: %s", val.v.(string))
101 }
102
103 subType = byte(i)
104 stFound = true
105 default:
106 return nil, 0, fmt.Errorf("invalid key in $binary object: %s", key)
107 }
108 }
109
110 if !bFound {
111 return nil, 0, errors.New("missing base64 field in $binary object")
112 }
113
114 if !stFound {
115 return nil, 0, errors.New("missing subType field in $binary object")
116
117 }
118
119 return b, subType, nil
120 }
121
122 func (ejv *extJSONValue) parseDBPointer() (ns string, oid primitive.ObjectID, err error) {
123 if ejv.t != bsontype.EmbeddedDocument {
124 return "", primitive.NilObjectID, fmt.Errorf("$dbPointer value should be object, but instead is %s", ejv.t)
125 }
126
127 dbpObj := ejv.v.(*extJSONObject)
128 oidFound := false
129 nsFound := false
130
131 for i, key := range dbpObj.keys {
132 val := dbpObj.values[i]
133
134 switch key {
135 case "$ref":
136 if nsFound {
137 return "", primitive.NilObjectID, errors.New("duplicate $ref key in $dbPointer")
138 }
139
140 if val.t != bsontype.String {
141 return "", primitive.NilObjectID, fmt.Errorf("$dbPointer $ref value should be string, but instead is %s", val.t)
142 }
143
144 ns = val.v.(string)
145 nsFound = true
146 case "$id":
147 if oidFound {
148 return "", primitive.NilObjectID, errors.New("duplicate $id key in $dbPointer")
149 }
150
151 if val.t != bsontype.String {
152 return "", primitive.NilObjectID, fmt.Errorf("$dbPointer $id value should be string, but instead is %s", val.t)
153 }
154
155 oid, err = primitive.ObjectIDFromHex(val.v.(string))
156 if err != nil {
157 return "", primitive.NilObjectID, err
158 }
159
160 oidFound = true
161 default:
162 return "", primitive.NilObjectID, fmt.Errorf("invalid key in $dbPointer object: %s", key)
163 }
164 }
165
166 if !nsFound {
167 return "", oid, errors.New("missing $ref field in $dbPointer object")
168 }
169
170 if !oidFound {
171 return "", oid, errors.New("missing $id field in $dbPointer object")
172 }
173
174 return ns, oid, nil
175 }
176
177 const (
178 rfc3339Milli = "2006-01-02T15:04:05.999Z07:00"
179 )
180
181 var (
182 timeFormats = []string{rfc3339Milli, "2006-01-02T15:04:05.999Z0700"}
183 )
184
185 func (ejv *extJSONValue) parseDateTime() (int64, error) {
186 switch ejv.t {
187 case bsontype.Int32:
188 return int64(ejv.v.(int32)), nil
189 case bsontype.Int64:
190 return ejv.v.(int64), nil
191 case bsontype.String:
192 return parseDatetimeString(ejv.v.(string))
193 case bsontype.EmbeddedDocument:
194 return parseDatetimeObject(ejv.v.(*extJSONObject))
195 default:
196 return 0, fmt.Errorf("$date value should be string or object, but instead is %s", ejv.t)
197 }
198 }
199
200 func parseDatetimeString(data string) (int64, error) {
201 var t time.Time
202 var err error
203
204 for _, format := range timeFormats {
205 t, err = time.Parse(format, data)
206 if err == nil {
207 break
208 }
209 }
210 if err != nil {
211 return 0, fmt.Errorf("invalid $date value string: %s", data)
212 }
213
214 return int64(primitive.NewDateTimeFromTime(t)), nil
215 }
216
217 func parseDatetimeObject(data *extJSONObject) (d int64, err error) {
218 dFound := false
219
220 for i, key := range data.keys {
221 val := data.values[i]
222
223 switch key {
224 case "$numberLong":
225 if dFound {
226 return 0, errors.New("duplicate $numberLong key in $date")
227 }
228
229 if val.t != bsontype.String {
230 return 0, fmt.Errorf("$date $numberLong field should be string, but instead is %s", val.t)
231 }
232
233 d, err = val.parseInt64()
234 if err != nil {
235 return 0, err
236 }
237 dFound = true
238 default:
239 return 0, fmt.Errorf("invalid key in $date object: %s", key)
240 }
241 }
242
243 if !dFound {
244 return 0, errors.New("missing $numberLong field in $date object")
245 }
246
247 return d, nil
248 }
249
250 func (ejv *extJSONValue) parseDecimal128() (primitive.Decimal128, error) {
251 if ejv.t != bsontype.String {
252 return primitive.Decimal128{}, fmt.Errorf("$numberDecimal value should be string, but instead is %s", ejv.t)
253 }
254
255 d, err := primitive.ParseDecimal128(ejv.v.(string))
256 if err != nil {
257 return primitive.Decimal128{}, fmt.Errorf("$invalid $numberDecimal string: %s", ejv.v.(string))
258 }
259
260 return d, nil
261 }
262
263 func (ejv *extJSONValue) parseDouble() (float64, error) {
264 if ejv.t == bsontype.Double {
265 return ejv.v.(float64), nil
266 }
267
268 if ejv.t != bsontype.String {
269 return 0, fmt.Errorf("$numberDouble value should be string, but instead is %s", ejv.t)
270 }
271
272 switch ejv.v.(string) {
273 case "Infinity":
274 return math.Inf(1), nil
275 case "-Infinity":
276 return math.Inf(-1), nil
277 case "NaN":
278 return math.NaN(), nil
279 }
280
281 f, err := strconv.ParseFloat(ejv.v.(string), 64)
282 if err != nil {
283 return 0, err
284 }
285
286 return f, nil
287 }
288
289 func (ejv *extJSONValue) parseInt32() (int32, error) {
290 if ejv.t == bsontype.Int32 {
291 return ejv.v.(int32), nil
292 }
293
294 if ejv.t != bsontype.String {
295 return 0, fmt.Errorf("$numberInt value should be string, but instead is %s", ejv.t)
296 }
297
298 i, err := strconv.ParseInt(ejv.v.(string), 10, 64)
299 if err != nil {
300 return 0, err
301 }
302
303 if i < math.MinInt32 || i > math.MaxInt32 {
304 return 0, fmt.Errorf("$numberInt value should be int32 but instead is int64: %d", i)
305 }
306
307 return int32(i), nil
308 }
309
310 func (ejv *extJSONValue) parseInt64() (int64, error) {
311 if ejv.t == bsontype.Int64 {
312 return ejv.v.(int64), nil
313 }
314
315 if ejv.t != bsontype.String {
316 return 0, fmt.Errorf("$numberLong value should be string, but instead is %s", ejv.t)
317 }
318
319 i, err := strconv.ParseInt(ejv.v.(string), 10, 64)
320 if err != nil {
321 return 0, err
322 }
323
324 return i, nil
325 }
326
327 func (ejv *extJSONValue) parseJavascript() (code string, err error) {
328 if ejv.t != bsontype.String {
329 return "", fmt.Errorf("$code value should be string, but instead is %s", ejv.t)
330 }
331
332 return ejv.v.(string), nil
333 }
334
335 func (ejv *extJSONValue) parseMinMaxKey(minmax string) error {
336 if ejv.t != bsontype.Int32 {
337 return fmt.Errorf("$%sKey value should be int32, but instead is %s", minmax, ejv.t)
338 }
339
340 if ejv.v.(int32) != 1 {
341 return fmt.Errorf("$%sKey value must be 1, but instead is %d", minmax, ejv.v.(int32))
342 }
343
344 return nil
345 }
346
347 func (ejv *extJSONValue) parseObjectID() (primitive.ObjectID, error) {
348 if ejv.t != bsontype.String {
349 return primitive.NilObjectID, fmt.Errorf("$oid value should be string, but instead is %s", ejv.t)
350 }
351
352 return primitive.ObjectIDFromHex(ejv.v.(string))
353 }
354
355 func (ejv *extJSONValue) parseRegex() (pattern, options string, err error) {
356 if ejv.t != bsontype.EmbeddedDocument {
357 return "", "", fmt.Errorf("$regularExpression value should be object, but instead is %s", ejv.t)
358 }
359
360 regexObj := ejv.v.(*extJSONObject)
361 patFound := false
362 optFound := false
363
364 for i, key := range regexObj.keys {
365 val := regexObj.values[i]
366
367 switch key {
368 case "pattern":
369 if patFound {
370 return "", "", errors.New("duplicate pattern key in $regularExpression")
371 }
372
373 if val.t != bsontype.String {
374 return "", "", fmt.Errorf("$regularExpression pattern value should be string, but instead is %s", val.t)
375 }
376
377 pattern = val.v.(string)
378 patFound = true
379 case "options":
380 if optFound {
381 return "", "", errors.New("duplicate options key in $regularExpression")
382 }
383
384 if val.t != bsontype.String {
385 return "", "", fmt.Errorf("$regularExpression options value should be string, but instead is %s", val.t)
386 }
387
388 options = val.v.(string)
389 optFound = true
390 default:
391 return "", "", fmt.Errorf("invalid key in $regularExpression object: %s", key)
392 }
393 }
394
395 if !patFound {
396 return "", "", errors.New("missing pattern field in $regularExpression object")
397 }
398
399 if !optFound {
400 return "", "", errors.New("missing options field in $regularExpression object")
401
402 }
403
404 return pattern, options, nil
405 }
406
407 func (ejv *extJSONValue) parseSymbol() (string, error) {
408 if ejv.t != bsontype.String {
409 return "", fmt.Errorf("$symbol value should be string, but instead is %s", ejv.t)
410 }
411
412 return ejv.v.(string), nil
413 }
414
415 func (ejv *extJSONValue) parseTimestamp() (t, i uint32, err error) {
416 if ejv.t != bsontype.EmbeddedDocument {
417 return 0, 0, fmt.Errorf("$timestamp value should be object, but instead is %s", ejv.t)
418 }
419
420 handleKey := func(key string, val *extJSONValue, flag bool) (uint32, error) {
421 if flag {
422 return 0, fmt.Errorf("duplicate %s key in $timestamp", key)
423 }
424
425 switch val.t {
426 case bsontype.Int32:
427 value := val.v.(int32)
428
429 if value < 0 {
430 return 0, fmt.Errorf("$timestamp %s number should be uint32: %d", key, value)
431 }
432
433 return uint32(value), nil
434 case bsontype.Int64:
435 value := val.v.(int64)
436 if value < 0 || value > int64(math.MaxUint32) {
437 return 0, fmt.Errorf("$timestamp %s number should be uint32: %d", key, value)
438 }
439
440 return uint32(value), nil
441 default:
442 return 0, fmt.Errorf("$timestamp %s value should be uint32, but instead is %s", key, val.t)
443 }
444 }
445
446 tsObj := ejv.v.(*extJSONObject)
447 tFound := false
448 iFound := false
449
450 for j, key := range tsObj.keys {
451 val := tsObj.values[j]
452
453 switch key {
454 case "t":
455 if t, err = handleKey(key, val, tFound); err != nil {
456 return 0, 0, err
457 }
458
459 tFound = true
460 case "i":
461 if i, err = handleKey(key, val, iFound); err != nil {
462 return 0, 0, err
463 }
464
465 iFound = true
466 default:
467 return 0, 0, fmt.Errorf("invalid key in $timestamp object: %s", key)
468 }
469 }
470
471 if !tFound {
472 return 0, 0, errors.New("missing t field in $timestamp object")
473 }
474
475 if !iFound {
476 return 0, 0, errors.New("missing i field in $timestamp object")
477 }
478
479 return t, i, nil
480 }
481
482 func (ejv *extJSONValue) parseUndefined() error {
483 if ejv.t != bsontype.Boolean {
484 return fmt.Errorf("undefined value should be boolean, but instead is %s", ejv.t)
485 }
486
487 if !ejv.v.(bool) {
488 return fmt.Errorf("$undefined balue boolean should be true, but instead is %v", ejv.v.(bool))
489 }
490
491 return nil
492 }
493
View as plain text