Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
aos
GitHub Repository: aos/grafana-agent
Path: blob/main/pkg/river/encoding/block.go
4095 views
1
package encoding
2
3
import (
4
"fmt"
5
"reflect"
6
"strings"
7
8
"github.com/grafana/agent/pkg/river/internal/reflectutil"
9
"github.com/grafana/agent/pkg/river/internal/rivertags"
10
"github.com/grafana/agent/pkg/river/internal/value"
11
)
12
13
// blockField represents the json representation of a river block.
14
type blockField struct {
15
field `json:",omitempty"`
16
ID string `json:"id,omitempty"`
17
Label string `json:"label,omitempty"`
18
Body []interface{} `json:"body,omitempty"`
19
}
20
21
func newBlock(namePrefix []string, reflectValue reflect.Value, f rivertags.Field) (*blockField, error) {
22
bf := &blockField{}
23
return bf, bf.convertBlock(namePrefix, reflectValue, f)
24
}
25
26
func (bf *blockField) hasValue() bool {
27
if bf == nil {
28
return false
29
}
30
return len(bf.Body) > 0
31
}
32
33
func (bf *blockField) convertBlock(namePrefix []string, reflectValue reflect.Value, f rivertags.Field) error {
34
for reflectValue.Kind() == reflect.Pointer {
35
if reflectValue.IsNil() {
36
return nil
37
}
38
reflectValue = reflectValue.Elem()
39
}
40
41
switch reflectValue.Kind() {
42
case reflect.Struct:
43
bf.Name = strings.Join(mergeStringSlices(namePrefix, f.Name), ".")
44
bf.Type = "block"
45
bf.Label = getBlockLabel(reflectValue)
46
47
fields, err := getFieldsForBlockStruct(namePrefix, reflectValue.Interface())
48
if err != nil {
49
return err
50
}
51
bf.Body = fields
52
return nil
53
54
case reflect.Map:
55
if reflectValue.Type().Key().Kind() != reflect.String {
56
return fmt.Errorf("convertBlock given unsupported map type; expected map[string]T, got %s", reflectValue.Type())
57
}
58
59
bf.Name = strings.Join(mergeStringSlices(namePrefix, f.Name), ".")
60
bf.Type = "block"
61
62
fields, err := getFieldsForBlockMap(reflectValue)
63
if err != nil {
64
return err
65
}
66
bf.Body = fields
67
return nil
68
69
default:
70
return fmt.Errorf("convertBlock can only be called on struct or map kinds, got %s", reflectValue.Kind())
71
}
72
}
73
74
// getBlockLabel returns the label for a given block.
75
func getBlockLabel(rv reflect.Value) string {
76
tags := rivertags.Get(rv.Type())
77
for _, tag := range tags {
78
if tag.Flags&rivertags.FlagLabel != 0 {
79
return reflectutil.Get(rv, tag).String()
80
}
81
}
82
83
return ""
84
}
85
86
func getFieldsForBlockStruct(namePrefix []string, input interface{}) ([]interface{}, error) {
87
val := value.Encode(input)
88
reflectVal := val.Reflect()
89
rt := rivertags.Get(reflectVal.Type())
90
var fields []interface{}
91
for _, t := range rt {
92
fieldRef := reflectutil.Get(reflectVal, t)
93
fieldVal := value.FromRaw(fieldRef)
94
95
if t.IsBlock() && (fieldRef.Kind() == reflect.Array || fieldRef.Kind() == reflect.Slice) {
96
for i := 0; i < fieldRef.Len(); i++ {
97
arrEle := fieldRef.Index(i).Interface()
98
bf, err := newBlock(namePrefix, reflect.ValueOf(arrEle), t)
99
if err != nil {
100
return nil, err
101
}
102
if bf.hasValue() {
103
fields = append(fields, bf)
104
}
105
}
106
} else if t.IsBlock() {
107
bf, err := newBlock(namePrefix, fieldRef, t)
108
109
if err != nil {
110
return nil, err
111
}
112
if bf.hasValue() {
113
fields = append(fields, bf)
114
}
115
} else if t.IsEnum() {
116
newPrefix := mergeStringSlices(namePrefix, t.Name)
117
118
for i := 0; i < fieldRef.Len(); i++ {
119
innerFields, err := getFieldsForEnum(newPrefix, fieldRef.Index(i))
120
if err != nil {
121
return nil, err
122
}
123
fields = append(fields, innerFields...)
124
}
125
} else if t.IsAttr() {
126
af, err := newAttribute(fieldVal, t)
127
if err != nil {
128
return nil, err
129
}
130
if af.hasValue() {
131
fields = append(fields, af)
132
}
133
} else if t.IsLabel() {
134
// Label is inherent in the block already so this can be a noop
135
continue
136
} else {
137
panic(fmt.Sprintf("river/encoding: unrecognized field %#v", t))
138
}
139
}
140
return fields, nil
141
}
142
143
func getFieldsForBlockMap(val reflect.Value) ([]interface{}, error) {
144
var fields []interface{}
145
146
it := val.MapRange()
147
for it.Next() {
148
// Make a fake field so newAttribute works properly.
149
field := rivertags.Field{
150
Name: []string{it.Key().String()},
151
Flags: rivertags.FlagAttr,
152
}
153
attr, err := newAttribute(value.FromRaw(it.Value()), field)
154
if err != nil {
155
return nil, err
156
}
157
158
fields = append(fields, attr)
159
}
160
161
return fields, nil
162
}
163
164
func mergeStringSlices(a, b []string) []string {
165
if len(a) == 0 {
166
return b
167
} else if len(b) == 0 {
168
return a
169
}
170
171
res := make([]string, 0, len(a)+len(b))
172
res = append(res, a...)
173
res = append(res, b...)
174
return res
175
}
176
177
func getFieldsForEnum(name []string, enumElement reflect.Value) ([]interface{}, error) {
178
var result []interface{}
179
180
for enumElement.Kind() == reflect.Pointer {
181
if enumElement.IsNil() {
182
return nil, nil
183
}
184
enumElement = enumElement.Elem()
185
}
186
187
fields := rivertags.Get(enumElement.Type())
188
189
// Find the first non-zero field and encode it as a block.
190
for _, field := range fields {
191
fieldVal := reflectutil.Get(enumElement, field)
192
if !fieldVal.IsValid() || fieldVal.IsZero() {
193
continue
194
}
195
196
bf, err := newBlock(name, fieldVal, field)
197
if err != nil {
198
return nil, err
199
}
200
if bf.hasValue() {
201
result = append(result, bf)
202
}
203
break
204
}
205
206
return result, nil
207
}
208
209