-
Notifications
You must be signed in to change notification settings - Fork 485
/
river.go
343 lines (323 loc) · 13.9 KB
/
river.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
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
// Package river implements a high-level API for decoding and encoding River
// configuration files. The mapping between River and Go values is described in
// the documentation for the Unmarshal and Marshal functions.
//
// Lower-level APIs which give more control over configuration evaluation are
// available in the inner packages. The implementation of this package is
// minimal and serves as a reference for how to consume the lower-level
// packages.
package river
import (
"bytes"
"io"
"github.com/grafana/agent/pkg/river/parser"
"github.com/grafana/agent/pkg/river/token/builder"
"github.com/grafana/agent/pkg/river/vm"
)
// Marshal returns the pretty-printed encoding of v as a River configuration
// file. v must be a Go struct with river struct tags which determine the
// structure of the resulting file.
//
// Marshal traverses the value v recursively, encoding each struct field as a
// River block or River attribute, based on the flags provided to the river
// struct tag.
//
// When a struct field represents a River block, Marshal creates a new block
// and recursively encodes the value as the body of the block. The name of the
// created block is taken from the name specified by the river struct tag.
//
// Struct fields which represent River blocks must be either a Go struct or a
// slice of Go structs. When the field is a Go struct, its value is encoded as
// a single block. When the field is a slice of Go structs, a block is created
// for each element in the slice.
//
// When encoding a block, if the inner Go struct has a struct field
// representing a River block label, the value of that field is used as the
// label name for the created block. Fields used for River block labels must be
// the string type. When specified, there must not be more than one struct
// field which represents a block label.
//
// The river tag specifies a name, possibly followed by a comma-separated list
// of options. The name must be empty if the provided options do not support a
// name being defined. The following provides examples for all supported struct
// field tags with their meanings:
//
// // Field appears as a block named "example". It will always appear in the
// // resulting encoding. When decoding, "example" is treated as a required
// // block and must be present in the source text.
// Field struct{...} `river:"example,block"`
//
// // Field appears as a set of blocks named "example." It will appear in the
// // resulting encoding if there is at least one element in the slice. When
// // decoding, "example" is treated as a required block and at least one
// // "example" block must be present in the source text.
// Field []struct{...} `river:"example,block"`
//
// // Field appears as block named "example." It will always appear in the
// // resulting encoding. When decoding, "example" is treated as an optional
// // block and can be omitted from the source text.
// Field struct{...} `river:"example,block,optional"`
//
// // Field appears as a set of blocks named "example." It will appear in the
// // resulting encoding if there is at least one element in the slice. When
// // decoding, "example" is treated as an optional block and can be omitted
// // from the source text.
// Field []struct{...} `river:"example,block,optional"`
//
// // Field appears as an attribute named "example." It will always appear in
// // the resulting encoding. When decoding, "example" is treated as a
// // required attribute and must be present in the source text.
// Field bool `river:"example,attr"`
//
// // Field appears as an attribute named "example." If the field's value is
// // the Go zero value, "example" is omitted from the resulting encoding.
// // When decoding, "example" is treated as an optional attribute and can be
// // omitted from the source text.
// Field bool `river:"example,attr,optional"`
//
// // The value of Field appears as the block label for the struct being
// // converted into a block. When decoding, a block label must be provided.
// Field string `river:",label"`
//
// // The inner attributes and blocks of Field are exposed as top-level
// // attributes and blocks of the outer struct.
// Field struct{...} `river:",squash"`
//
// // Field appears as a set of blocks starting with "example.". Only the
// // first set element in the struct will be encoded. Each field in struct
// // must be a block. The name of the block is prepended to the enum name.
// // When decoding, enum blocks are treated as optional blocks and can be
// // omitted from the source text.
// Field []struct{...} `river:"example,enum"`
//
// // Field is equivalent to `river:"example,enum"`.
// Field []struct{...} `river:"example,enum,optional"`
//
// If a river tag specifies a required or optional block, the name is permitted
// to contain period `.` characters.
//
// Marshal will panic if it encounters a struct with invalid river tags.
//
// When a struct field represents a River attribute, Marshal encodes the struct
// value as a River value. The attribute name will be taken from the name
// specified by the river struct tag. See MarshalValue for the rules used to
// convert a Go value into a River value.
func Marshal(v interface{}) ([]byte, error) {
var buf bytes.Buffer
if err := NewEncoder(&buf).Encode(v); err != nil {
return nil, err
}
return buf.Bytes(), nil
}
// MarshalValue returns the pretty-printed encoding of v as a River value.
//
// MarshalValue traverses the value v recursively. If an encountered value
// implements the encoding.TextMarshaler interface, MarshalValue calls its
// MarshalText method and encodes the result as a River string. If a value
// implements the Capsule interface, it always encodes as a River capsule
// value.
//
// Otherwise, MarshalValue uses the following type-dependent default encodings:
//
// Boolean values encode to River bools.
//
// Floating point, integer, and Number values encode to River numbers.
//
// String values encode to River strings.
//
// Array and slice values encode to River arrays, except that []byte is
// converted into a River string. Nil slices encode as an empty array and nil
// []byte slices encode as an empty string.
//
// Structs encode to River objects, using Go struct field tags to determine the
// resulting structure of the River object. Each exported struct field with a
// river tag becomes an object field, using the tag name as the field name.
// Other struct fields are ignored. If no struct field has a river tag, the
// struct encodes to a River capsule instead.
//
// Function values encode to River functions, which appear in the resulting
// text as strings formatted as "function(GO_TYPE)".
//
// All other Go values encode to River capsules, which appear in the resulting
// text as strings formatted as "capsule(GO_TYPE)".
//
// The river tag specifies the field name, possibly followed by a
// comma-separated list of options. The following provides examples for all
// supported struct field tags with their meanings:
//
// // Field appears as an object field named "my_name". It will always
// // appear in the resulting encoding. When decoding, "my_name" is treated
// // as a required attribute and must be present in the source text.
// Field bool `river:"my_name,attr"`
//
// // Field appears as an object field named "my_name". If the field's value
// // is the Go zero value, "example" is omitted from the resulting encoding.
// // When decoding, "my_name" is treated as an optional attribute and can be
// // omitted from the source text.
// Field bool `river:"my_name,attr,optional"`
func MarshalValue(v interface{}) ([]byte, error) {
var buf bytes.Buffer
if err := NewEncoder(&buf).EncodeValue(v); err != nil {
return nil, err
}
return buf.Bytes(), nil
}
// Encoder writes River configuration to an output stream. Call NewEncoder to
// create instances of Encoder.
type Encoder struct {
w io.Writer
}
// NewEncoder returns a new Encoder which writes configuration to w.
func NewEncoder(w io.Writer) *Encoder {
return &Encoder{w: w}
}
// Encode converts the value pointed to by v into a River configuration file
// and writes the result to the Decoder's output stream.
//
// See the documentation for Marshal for details about the conversion of Go
// values into River configuration.
func (enc *Encoder) Encode(v interface{}) error {
f := builder.NewFile()
f.Body().AppendFrom(v)
_, err := f.WriteTo(enc.w)
return err
}
// EncodeValue converts the value pointed to by v into a River value and writes
// the result to the Decoder's output stream.
//
// See the documentation for MarshalValue for details about the conversion of
// Go values into River values.
func (enc *Encoder) EncodeValue(v interface{}) error {
expr := builder.NewExpr()
expr.SetValue(v)
_, err := expr.WriteTo(enc.w)
return err
}
// Unmarshal converts the River configuration file specified by in and stores
// it in the struct value pointed to by v. If v is nil or not a pointer,
// Unmarshal panics. The configuration specified by in may use expressions to
// compute values while unmarshaling. Refer to the River language documentation
// for the list of valid formatting and expression rules.
//
// Unmarshal uses the inverse of the encoding rules that Marshal uses,
// allocating maps, slices, and pointers as necessary.
//
// To unmarshal a River body into a map[string]T, Unmarshal assigns each
// attribute to a key in the map, and decodes the attribute's value as the
// value for the map entry. Only attribute statements are allowed when
// unmarshaling into a map.
//
// To unmarshal a River body into a struct, Unmarshal matches incoming
// attributes and blocks to the river struct tags specified by v. Incoming
// attribute and blocks which do not match to a river struct tag cause a
// decoding error. Additionally, any attribute or block marked as required by
// the river struct tag that are not present in the source text will generate a
// decoding error.
//
// To unmarshal a list of River blocks into a slice, Unmarshal resets the slice
// length to zero and then appends each element to the slice.
//
// To unmarshal a list of River blocks into a Go array, Unmarshal decodes each
// block into the corresponding Go array element. If the number of River blocks
// does not match the length of the Go array, a decoding error is returned.
//
// Unmarshal follows the rules specified by UnmarshalValue when unmarshaling
// the value of an attribute.
func Unmarshal(in []byte, v interface{}) error {
dec := NewDecoder(bytes.NewReader(in))
return dec.Decode(v)
}
// UnmarshalValue converts the River configuration file specified by in and
// stores it in the value pointed to by v. If v is nil or not a pointer,
// UnmarshalValue panics. The configuration specified by in may use expressions
// to compute values while unmarshaling. Refer to the River language
// documentation for the list of valid formatting and expression rules.
//
// Unmarshal uses the inverse of the encoding rules that MarshalValue uses,
// allocating maps, slices, and pointers as necessary, with the following
// additional rules:
//
// After converting a River value into its Go value counterpart, the Go value
// may be converted into a capsule if the capsule type implements
// ConvertibleIntoCapsule.
//
// To unmarshal a River object into a struct, UnmarshalValue matches incoming
// object fields to the river struct tags specified by v. Incoming object
// fields which do not match to a river struct tag cause a decoding error.
// Additionally, any object field marked as required by the river struct
// tag that are not present in the source text will generate a decoding error.
//
// To unmarshal River into an interface value, Unmarshal stores one of the
// following:
//
// - bool, for River bools
// - float64, for River numbers
// - string, for River strings
// - []interface{}, for River arrays
// - map[string]interface{}, for River objects
//
// Capsule and function types will retain their original type when decoding
// into an interface value.
//
// To unmarshal a River array into a slice, Unmarshal resets the slice length
// to zero and then appends each element to the slice.
//
// To unmarshal a River array into a Go array, Unmarshal decodes River array
// elements into the corresponding Go array element. If the number of River
// elements does not match the length of the Go array, a decoding error is
// returned.
//
// To unmarshal a River object into a Map, Unmarshal establishes a map to use.
// If the map is nil, Unmarshal allocates a new map. Otherwise, Unmarshal
// reuses the existing map, keeping existing entries. Unmarshal then stores
// key-value pairs from the River object into the map. The map's key type must
// be string.
func UnmarshalValue(in []byte, v interface{}) error {
dec := NewDecoder(bytes.NewReader(in))
return dec.DecodeValue(v)
}
// Decoder reads River configuration from an input stream. Call NewDecoder to
// create instances of Decoder.
type Decoder struct {
r io.Reader
}
// NewDecoder returns a new Decoder which reads configuration from r.
func NewDecoder(r io.Reader) *Decoder {
return &Decoder{r: r}
}
// Decode reads the River-encoded file from the Decoder's input and stores it
// in the value pointed to by v. Data will be read from the Decoder's input
// until EOF is reached.
//
// See the documentation for Unmarshal for details about the conversion of River
// configuration into Go values.
func (dec *Decoder) Decode(v interface{}) error {
bb, err := io.ReadAll(dec.r)
if err != nil {
return err
}
f, err := parser.ParseFile("", bb)
if err != nil {
return err
}
eval := vm.New(f)
return eval.Evaluate(nil, v)
}
// DecodeValue reads the River-encoded expression from the Decoder's input and
// stores it in the value pointed to by v. Data will be read from the Decoder's
// input until EOF is reached.
//
// See the documentation for UnmarshalValue for details about the conversion of
// River values into Go values.
func (dec *Decoder) DecodeValue(v interface{}) error {
bb, err := io.ReadAll(dec.r)
if err != nil {
return err
}
f, err := parser.ParseExpression(string(bb))
if err != nil {
return err
}
eval := vm.New(f)
return eval.Evaluate(nil, v)
}