mirror of
https://github.com/kubernetes-sigs/prometheus-adapter.git
synced 2026-04-06 17:57:51 +00:00
Travis seems to be having issues pulling deps, so we'll have to check in the vendor directory and prevent the makefile from trying to regenerate it normally.
362 lines
8.6 KiB
Go
362 lines
8.6 KiB
Go
package yaml
|
|
|
|
import (
|
|
"encoding"
|
|
"fmt"
|
|
"io"
|
|
"reflect"
|
|
"regexp"
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
"unicode/utf8"
|
|
)
|
|
|
|
type encoder struct {
|
|
emitter yaml_emitter_t
|
|
event yaml_event_t
|
|
out []byte
|
|
flow bool
|
|
// doneInit holds whether the initial stream_start_event has been
|
|
// emitted.
|
|
doneInit bool
|
|
}
|
|
|
|
func newEncoder() *encoder {
|
|
e := &encoder{}
|
|
yaml_emitter_initialize(&e.emitter)
|
|
yaml_emitter_set_output_string(&e.emitter, &e.out)
|
|
yaml_emitter_set_unicode(&e.emitter, true)
|
|
return e
|
|
}
|
|
|
|
func newEncoderWithWriter(w io.Writer) *encoder {
|
|
e := &encoder{}
|
|
yaml_emitter_initialize(&e.emitter)
|
|
yaml_emitter_set_output_writer(&e.emitter, w)
|
|
yaml_emitter_set_unicode(&e.emitter, true)
|
|
return e
|
|
}
|
|
|
|
func (e *encoder) init() {
|
|
if e.doneInit {
|
|
return
|
|
}
|
|
yaml_stream_start_event_initialize(&e.event, yaml_UTF8_ENCODING)
|
|
e.emit()
|
|
e.doneInit = true
|
|
}
|
|
|
|
func (e *encoder) finish() {
|
|
e.emitter.open_ended = false
|
|
yaml_stream_end_event_initialize(&e.event)
|
|
e.emit()
|
|
}
|
|
|
|
func (e *encoder) destroy() {
|
|
yaml_emitter_delete(&e.emitter)
|
|
}
|
|
|
|
func (e *encoder) emit() {
|
|
// This will internally delete the e.event value.
|
|
e.must(yaml_emitter_emit(&e.emitter, &e.event))
|
|
}
|
|
|
|
func (e *encoder) must(ok bool) {
|
|
if !ok {
|
|
msg := e.emitter.problem
|
|
if msg == "" {
|
|
msg = "unknown problem generating YAML content"
|
|
}
|
|
failf("%s", msg)
|
|
}
|
|
}
|
|
|
|
func (e *encoder) marshalDoc(tag string, in reflect.Value) {
|
|
e.init()
|
|
yaml_document_start_event_initialize(&e.event, nil, nil, true)
|
|
e.emit()
|
|
e.marshal(tag, in)
|
|
yaml_document_end_event_initialize(&e.event, true)
|
|
e.emit()
|
|
}
|
|
|
|
func (e *encoder) marshal(tag string, in reflect.Value) {
|
|
if !in.IsValid() || in.Kind() == reflect.Ptr && in.IsNil() {
|
|
e.nilv()
|
|
return
|
|
}
|
|
iface := in.Interface()
|
|
switch m := iface.(type) {
|
|
case time.Time, *time.Time:
|
|
// Although time.Time implements TextMarshaler,
|
|
// we don't want to treat it as a string for YAML
|
|
// purposes because YAML has special support for
|
|
// timestamps.
|
|
case Marshaler:
|
|
v, err := m.MarshalYAML()
|
|
if err != nil {
|
|
fail(err)
|
|
}
|
|
if v == nil {
|
|
e.nilv()
|
|
return
|
|
}
|
|
in = reflect.ValueOf(v)
|
|
case encoding.TextMarshaler:
|
|
text, err := m.MarshalText()
|
|
if err != nil {
|
|
fail(err)
|
|
}
|
|
in = reflect.ValueOf(string(text))
|
|
case nil:
|
|
e.nilv()
|
|
return
|
|
}
|
|
switch in.Kind() {
|
|
case reflect.Interface:
|
|
e.marshal(tag, in.Elem())
|
|
case reflect.Map:
|
|
e.mapv(tag, in)
|
|
case reflect.Ptr:
|
|
if in.Type() == ptrTimeType {
|
|
e.timev(tag, in.Elem())
|
|
} else {
|
|
e.marshal(tag, in.Elem())
|
|
}
|
|
case reflect.Struct:
|
|
if in.Type() == timeType {
|
|
e.timev(tag, in)
|
|
} else {
|
|
e.structv(tag, in)
|
|
}
|
|
case reflect.Slice, reflect.Array:
|
|
if in.Type().Elem() == mapItemType {
|
|
e.itemsv(tag, in)
|
|
} else {
|
|
e.slicev(tag, in)
|
|
}
|
|
case reflect.String:
|
|
e.stringv(tag, in)
|
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
if in.Type() == durationType {
|
|
e.stringv(tag, reflect.ValueOf(iface.(time.Duration).String()))
|
|
} else {
|
|
e.intv(tag, in)
|
|
}
|
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
|
e.uintv(tag, in)
|
|
case reflect.Float32, reflect.Float64:
|
|
e.floatv(tag, in)
|
|
case reflect.Bool:
|
|
e.boolv(tag, in)
|
|
default:
|
|
panic("cannot marshal type: " + in.Type().String())
|
|
}
|
|
}
|
|
|
|
func (e *encoder) mapv(tag string, in reflect.Value) {
|
|
e.mappingv(tag, func() {
|
|
keys := keyList(in.MapKeys())
|
|
sort.Sort(keys)
|
|
for _, k := range keys {
|
|
e.marshal("", k)
|
|
e.marshal("", in.MapIndex(k))
|
|
}
|
|
})
|
|
}
|
|
|
|
func (e *encoder) itemsv(tag string, in reflect.Value) {
|
|
e.mappingv(tag, func() {
|
|
slice := in.Convert(reflect.TypeOf([]MapItem{})).Interface().([]MapItem)
|
|
for _, item := range slice {
|
|
e.marshal("", reflect.ValueOf(item.Key))
|
|
e.marshal("", reflect.ValueOf(item.Value))
|
|
}
|
|
})
|
|
}
|
|
|
|
func (e *encoder) structv(tag string, in reflect.Value) {
|
|
sinfo, err := getStructInfo(in.Type())
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
e.mappingv(tag, func() {
|
|
for _, info := range sinfo.FieldsList {
|
|
var value reflect.Value
|
|
if info.Inline == nil {
|
|
value = in.Field(info.Num)
|
|
} else {
|
|
value = in.FieldByIndex(info.Inline)
|
|
}
|
|
if info.OmitEmpty && isZero(value) {
|
|
continue
|
|
}
|
|
e.marshal("", reflect.ValueOf(info.Key))
|
|
e.flow = info.Flow
|
|
e.marshal("", value)
|
|
}
|
|
if sinfo.InlineMap >= 0 {
|
|
m := in.Field(sinfo.InlineMap)
|
|
if m.Len() > 0 {
|
|
e.flow = false
|
|
keys := keyList(m.MapKeys())
|
|
sort.Sort(keys)
|
|
for _, k := range keys {
|
|
if _, found := sinfo.FieldsMap[k.String()]; found {
|
|
panic(fmt.Sprintf("Can't have key %q in inlined map; conflicts with struct field", k.String()))
|
|
}
|
|
e.marshal("", k)
|
|
e.flow = false
|
|
e.marshal("", m.MapIndex(k))
|
|
}
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
func (e *encoder) mappingv(tag string, f func()) {
|
|
implicit := tag == ""
|
|
style := yaml_BLOCK_MAPPING_STYLE
|
|
if e.flow {
|
|
e.flow = false
|
|
style = yaml_FLOW_MAPPING_STYLE
|
|
}
|
|
yaml_mapping_start_event_initialize(&e.event, nil, []byte(tag), implicit, style)
|
|
e.emit()
|
|
f()
|
|
yaml_mapping_end_event_initialize(&e.event)
|
|
e.emit()
|
|
}
|
|
|
|
func (e *encoder) slicev(tag string, in reflect.Value) {
|
|
implicit := tag == ""
|
|
style := yaml_BLOCK_SEQUENCE_STYLE
|
|
if e.flow {
|
|
e.flow = false
|
|
style = yaml_FLOW_SEQUENCE_STYLE
|
|
}
|
|
e.must(yaml_sequence_start_event_initialize(&e.event, nil, []byte(tag), implicit, style))
|
|
e.emit()
|
|
n := in.Len()
|
|
for i := 0; i < n; i++ {
|
|
e.marshal("", in.Index(i))
|
|
}
|
|
e.must(yaml_sequence_end_event_initialize(&e.event))
|
|
e.emit()
|
|
}
|
|
|
|
// isBase60 returns whether s is in base 60 notation as defined in YAML 1.1.
|
|
//
|
|
// The base 60 float notation in YAML 1.1 is a terrible idea and is unsupported
|
|
// in YAML 1.2 and by this package, but these should be marshalled quoted for
|
|
// the time being for compatibility with other parsers.
|
|
func isBase60Float(s string) (result bool) {
|
|
// Fast path.
|
|
if s == "" {
|
|
return false
|
|
}
|
|
c := s[0]
|
|
if !(c == '+' || c == '-' || c >= '0' && c <= '9') || strings.IndexByte(s, ':') < 0 {
|
|
return false
|
|
}
|
|
// Do the full match.
|
|
return base60float.MatchString(s)
|
|
}
|
|
|
|
// From http://yaml.org/type/float.html, except the regular expression there
|
|
// is bogus. In practice parsers do not enforce the "\.[0-9_]*" suffix.
|
|
var base60float = regexp.MustCompile(`^[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+(?:\.[0-9_]*)?$`)
|
|
|
|
func (e *encoder) stringv(tag string, in reflect.Value) {
|
|
var style yaml_scalar_style_t
|
|
s := in.String()
|
|
canUsePlain := true
|
|
switch {
|
|
case !utf8.ValidString(s):
|
|
if tag == yaml_BINARY_TAG {
|
|
failf("explicitly tagged !!binary data must be base64-encoded")
|
|
}
|
|
if tag != "" {
|
|
failf("cannot marshal invalid UTF-8 data as %s", shortTag(tag))
|
|
}
|
|
// It can't be encoded directly as YAML so use a binary tag
|
|
// and encode it as base64.
|
|
tag = yaml_BINARY_TAG
|
|
s = encodeBase64(s)
|
|
case tag == "":
|
|
// Check to see if it would resolve to a specific
|
|
// tag when encoded unquoted. If it doesn't,
|
|
// there's no need to quote it.
|
|
rtag, _ := resolve("", s)
|
|
canUsePlain = rtag == yaml_STR_TAG && !isBase60Float(s)
|
|
}
|
|
// Note: it's possible for user code to emit invalid YAML
|
|
// if they explicitly specify a tag and a string containing
|
|
// text that's incompatible with that tag.
|
|
switch {
|
|
case strings.Contains(s, "\n"):
|
|
style = yaml_LITERAL_SCALAR_STYLE
|
|
case canUsePlain:
|
|
style = yaml_PLAIN_SCALAR_STYLE
|
|
default:
|
|
style = yaml_DOUBLE_QUOTED_SCALAR_STYLE
|
|
}
|
|
e.emitScalar(s, "", tag, style)
|
|
}
|
|
|
|
func (e *encoder) boolv(tag string, in reflect.Value) {
|
|
var s string
|
|
if in.Bool() {
|
|
s = "true"
|
|
} else {
|
|
s = "false"
|
|
}
|
|
e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE)
|
|
}
|
|
|
|
func (e *encoder) intv(tag string, in reflect.Value) {
|
|
s := strconv.FormatInt(in.Int(), 10)
|
|
e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE)
|
|
}
|
|
|
|
func (e *encoder) uintv(tag string, in reflect.Value) {
|
|
s := strconv.FormatUint(in.Uint(), 10)
|
|
e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE)
|
|
}
|
|
|
|
func (e *encoder) timev(tag string, in reflect.Value) {
|
|
t := in.Interface().(time.Time)
|
|
s := t.Format(time.RFC3339Nano)
|
|
e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE)
|
|
}
|
|
|
|
func (e *encoder) floatv(tag string, in reflect.Value) {
|
|
// Issue #352: When formatting, use the precision of the underlying value
|
|
precision := 64
|
|
if in.Kind() == reflect.Float32 {
|
|
precision = 32
|
|
}
|
|
|
|
s := strconv.FormatFloat(in.Float(), 'g', -1, precision)
|
|
switch s {
|
|
case "+Inf":
|
|
s = ".inf"
|
|
case "-Inf":
|
|
s = "-.inf"
|
|
case "NaN":
|
|
s = ".nan"
|
|
}
|
|
e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE)
|
|
}
|
|
|
|
func (e *encoder) nilv() {
|
|
e.emitScalar("null", "", "", yaml_PLAIN_SCALAR_STYLE)
|
|
}
|
|
|
|
func (e *encoder) emitScalar(value, anchor, tag string, style yaml_scalar_style_t) {
|
|
implicit := tag == ""
|
|
e.must(yaml_scalar_event_initialize(&e.event, []byte(anchor), []byte(tag), []byte(value), implicit, implicit, style))
|
|
e.emit()
|
|
}
|