This repository has been archived by the owner on Feb 21, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 230
/
codec.go
84 lines (69 loc) · 2.09 KB
/
codec.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
package api
import (
"encoding/json"
"io"
"github.com/pkg/errors"
)
// newJSONCodec creates a codec to stream a JSON array of objects, each of which represents a record.
// This returns an error if the data stream does not contain the start of a valid JSON array.
func newJSONCodec(r io.Reader) (*jsonCodec, error) {
// Create a JSON decoder.
d := json.NewDecoder(r)
// Enable the json.Number type in parsed output.
// This allows us to retain precision on numbers, rather than converting them to floating point.
d.UseNumber()
// Do not allow unknown fields.
d.DisallowUnknownFields()
// Read the opening bracket of the array.
token, err := d.Token()
if err != nil {
if err == io.EOF {
// The input included nothing (except maybe whitespace).
// This is not expected.
err = io.ErrUnexpectedEOF
}
return nil, errors.Wrap(err, "parsing start of JSON data")
}
switch token {
case json.Delim('['):
// This is the start of the array of records.
case json.Delim('{'):
// The input is a JSON object instead of an array.
// This seems like an easy user error, so provide a descriptive error.
return nil, errors.New("expected an array of records but found an object")
default:
// This is a number or something.
return nil, errors.Errorf("expected array of records but got %v", token)
}
return &jsonCodec{d: d}, nil
}
type record struct {
PrimaryKey interface{} `json:"primary-key"`
Values map[string]interface{} `json:"values"`
}
type jsonCodec struct {
d *json.Decoder
}
func (c *jsonCodec) Next() (record, error) {
if !c.d.More() {
// There are no more records in the stream.
// Read the ']'.
_, err := c.d.Token()
if err != nil {
if err == io.EOF {
// If the closing bracket is missing, then the stream was terminated prematurely.
err = io.ErrUnexpectedEOF
}
return record{}, errors.Wrap(err, "parsing end of record array")
}
// There are no more records.
return record{}, io.EOF
}
// Parse the record.
var r record
err := c.d.Decode(&r)
if err != nil {
return record{}, errors.Wrap(err, "parsing record")
}
return r, nil
}