-
Notifications
You must be signed in to change notification settings - Fork 154
/
encode.go
140 lines (136 loc) · 3.38 KB
/
encode.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
package json
import (
"context"
"encoding/json"
"strings"
"time"
"github.com/influxdata/flux/codes"
"github.com/influxdata/flux/internal/errors"
"github.com/influxdata/flux/runtime"
"github.com/influxdata/flux/semantic"
"github.com/influxdata/flux/values"
)
func init() {
runtime.RegisterPackageValue("json", "encode", values.NewFunction(
"encode",
runtime.MustLookupBuiltinType("json", "encode"),
func(ctx context.Context, args values.Object) (values.Value, error) {
v, ok := args.Get("v")
if !ok {
return nil, errors.New(codes.Invalid, "missing parameter \"v\"")
}
val, err := convertValue(v)
if err != nil {
return nil, err
}
bytes, err := json.Marshal(val)
if err != nil {
return nil, err
}
return values.NewBytes(bytes), nil
},
false,
))
}
func convertValue(v values.Value) (interface{}, error) {
if v.IsNull() {
return nil, nil
}
switch n := v.Type().Nature(); n {
case semantic.String:
return v.Str(), nil
case semantic.Bytes:
return v.Bytes(), nil
case semantic.Int:
return v.Int(), nil
case semantic.UInt:
return v.UInt(), nil
case semantic.Float:
return v.Float(), nil
case semantic.Bool:
return v.Bool(), nil
case semantic.Time:
return v.Time().Time().Format(time.RFC3339Nano), nil
case semantic.Duration:
return v.Duration(), nil
case semantic.Regexp:
return v.Regexp().String(), nil
case semantic.Array:
arr := v.Array()
a := make([]interface{}, arr.Len())
var rangeErr error
arr.Range(func(i int, v values.Value) {
if rangeErr != nil {
return // short circuit if we already hit an error
}
val, err := convertValue(v)
if err != nil {
rangeErr = err
return
}
a[i] = val
})
if rangeErr != nil {
return nil, rangeErr
}
return a, nil
case semantic.Stream:
return nil, errors.New(
codes.Invalid,
"got table stream instead of array. "+
"Try using tableFind() or findRecord() to extract data from stream")
case semantic.Object:
obj := v.Object()
o := make(map[string]interface{}, obj.Len())
var rangeErr error
obj.Range(func(k string, v values.Value) {
if rangeErr != nil {
return // short circuit if we already hit an error
}
val, err := convertValue(v)
if err != nil {
rangeErr = err
return
}
o[k] = val
})
if rangeErr != nil {
return nil, rangeErr
}
return o, nil
case semantic.Function:
return nil, errors.New(codes.Invalid, "cannot encode a function value")
case semantic.Dictionary:
dict := v.Dict()
// Go JSON encoder requires that map key type is either a primitive type or implements encoding.TextMarshaler interface.
// Since Go maps are encoded as JSON objects with string keys (https://www.json.org/json-en.html), and dictionary keys
// are primitive Flux types, we can safely convert Flux dictionary to Go map with string keys.
d := make(map[string]interface{}, dict.Len())
var rangeErr error
var b strings.Builder
dict.Range(func(k, v values.Value) {
if rangeErr != nil {
return // short circuit if we already hit an error
}
b.Reset()
err := values.Display(&b, k)
if err != nil {
rangeErr = err
return
}
key := b.String()
val, err := convertValue(v)
if err != nil {
rangeErr = err
return
}
d[key] = val
})
if rangeErr != nil {
return nil, rangeErr
}
return d, nil
default:
return nil, errors.Newf(codes.Unknown, "unknown nature %v", n)
}
}