aboutsummaryrefslogtreecommitdiff
path: root/vendor/github.com/go-logfmt/logfmt/encode.go
diff options
context:
space:
mode:
authorRaúl Benencia <raul@thousandeyes.com>2018-04-13 16:30:31 -0700
committerRaúl Benencia <raul@thousandeyes.com>2018-05-11 15:02:34 -0700
commit77c172b823b64ebface655681ab0749b9d2f7081 (patch)
tree09c13e626eb95ae1d33e76ed683172eab1ab6c96 /vendor/github.com/go-logfmt/logfmt/encode.go
First public commit
Diffstat (limited to 'vendor/github.com/go-logfmt/logfmt/encode.go')
-rw-r--r--vendor/github.com/go-logfmt/logfmt/encode.go321
1 files changed, 321 insertions, 0 deletions
diff --git a/vendor/github.com/go-logfmt/logfmt/encode.go b/vendor/github.com/go-logfmt/logfmt/encode.go
new file mode 100644
index 0000000..55f1603
--- /dev/null
+++ b/vendor/github.com/go-logfmt/logfmt/encode.go
@@ -0,0 +1,321 @@
+package logfmt
+
+import (
+ "bytes"
+ "encoding"
+ "errors"
+ "fmt"
+ "io"
+ "reflect"
+ "strings"
+ "unicode/utf8"
+)
+
+// MarshalKeyvals returns the logfmt encoding of keyvals, a variadic sequence
+// of alternating keys and values.
+func MarshalKeyvals(keyvals ...interface{}) ([]byte, error) {
+ buf := &bytes.Buffer{}
+ if err := NewEncoder(buf).EncodeKeyvals(keyvals...); err != nil {
+ return nil, err
+ }
+ return buf.Bytes(), nil
+}
+
+// An Encoder writes logfmt data to an output stream.
+type Encoder struct {
+ w io.Writer
+ scratch bytes.Buffer
+ needSep bool
+}
+
+// NewEncoder returns a new encoder that writes to w.
+func NewEncoder(w io.Writer) *Encoder {
+ return &Encoder{
+ w: w,
+ }
+}
+
+var (
+ space = []byte(" ")
+ equals = []byte("=")
+ newline = []byte("\n")
+ null = []byte("null")
+)
+
+// EncodeKeyval writes the logfmt encoding of key and value to the stream. A
+// single space is written before the second and subsequent keys in a record.
+// Nothing is written if a non-nil error is returned.
+func (enc *Encoder) EncodeKeyval(key, value interface{}) error {
+ enc.scratch.Reset()
+ if enc.needSep {
+ if _, err := enc.scratch.Write(space); err != nil {
+ return err
+ }
+ }
+ if err := writeKey(&enc.scratch, key); err != nil {
+ return err
+ }
+ if _, err := enc.scratch.Write(equals); err != nil {
+ return err
+ }
+ if err := writeValue(&enc.scratch, value); err != nil {
+ return err
+ }
+ _, err := enc.w.Write(enc.scratch.Bytes())
+ enc.needSep = true
+ return err
+}
+
+// EncodeKeyvals writes the logfmt encoding of keyvals to the stream. Keyvals
+// is a variadic sequence of alternating keys and values. Keys of unsupported
+// type are skipped along with their corresponding value. Values of
+// unsupported type or that cause a MarshalerError are replaced by their error
+// but do not cause EncodeKeyvals to return an error. If a non-nil error is
+// returned some key/value pairs may not have be written.
+func (enc *Encoder) EncodeKeyvals(keyvals ...interface{}) error {
+ if len(keyvals) == 0 {
+ return nil
+ }
+ if len(keyvals)%2 == 1 {
+ keyvals = append(keyvals, nil)
+ }
+ for i := 0; i < len(keyvals); i += 2 {
+ k, v := keyvals[i], keyvals[i+1]
+ err := enc.EncodeKeyval(k, v)
+ if err == ErrUnsupportedKeyType {
+ continue
+ }
+ if _, ok := err.(*MarshalerError); ok || err == ErrUnsupportedValueType {
+ v = err
+ err = enc.EncodeKeyval(k, v)
+ }
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// MarshalerError represents an error encountered while marshaling a value.
+type MarshalerError struct {
+ Type reflect.Type
+ Err error
+}
+
+func (e *MarshalerError) Error() string {
+ return "error marshaling value of type " + e.Type.String() + ": " + e.Err.Error()
+}
+
+// ErrNilKey is returned by Marshal functions and Encoder methods if a key is
+// a nil interface or pointer value.
+var ErrNilKey = errors.New("nil key")
+
+// ErrInvalidKey is returned by Marshal functions and Encoder methods if a key
+// contains an invalid character.
+var ErrInvalidKey = errors.New("invalid key")
+
+// ErrUnsupportedKeyType is returned by Encoder methods if a key has an
+// unsupported type.
+var ErrUnsupportedKeyType = errors.New("unsupported key type")
+
+// ErrUnsupportedValueType is returned by Encoder methods if a value has an
+// unsupported type.
+var ErrUnsupportedValueType = errors.New("unsupported value type")
+
+func writeKey(w io.Writer, key interface{}) error {
+ if key == nil {
+ return ErrNilKey
+ }
+
+ switch k := key.(type) {
+ case string:
+ return writeStringKey(w, k)
+ case []byte:
+ if k == nil {
+ return ErrNilKey
+ }
+ return writeBytesKey(w, k)
+ case encoding.TextMarshaler:
+ kb, err := safeMarshal(k)
+ if err != nil {
+ return err
+ }
+ if kb == nil {
+ return ErrNilKey
+ }
+ return writeBytesKey(w, kb)
+ case fmt.Stringer:
+ ks, ok := safeString(k)
+ if !ok {
+ return ErrNilKey
+ }
+ return writeStringKey(w, ks)
+ default:
+ rkey := reflect.ValueOf(key)
+ switch rkey.Kind() {
+ case reflect.Array, reflect.Chan, reflect.Func, reflect.Map, reflect.Slice, reflect.Struct:
+ return ErrUnsupportedKeyType
+ case reflect.Ptr:
+ if rkey.IsNil() {
+ return ErrNilKey
+ }
+ return writeKey(w, rkey.Elem().Interface())
+ }
+ return writeStringKey(w, fmt.Sprint(k))
+ }
+}
+
+func invalidKeyRune(r rune) bool {
+ return r <= ' ' || r == '=' || r == '"' || r == utf8.RuneError
+}
+
+func invalidKeyString(key string) bool {
+ return len(key) == 0 || strings.IndexFunc(key, invalidKeyRune) != -1
+}
+
+func invalidKey(key []byte) bool {
+ return len(key) == 0 || bytes.IndexFunc(key, invalidKeyRune) != -1
+}
+
+func writeStringKey(w io.Writer, key string) error {
+ if invalidKeyString(key) {
+ return ErrInvalidKey
+ }
+ _, err := io.WriteString(w, key)
+ return err
+}
+
+func writeBytesKey(w io.Writer, key []byte) error {
+ if invalidKey(key) {
+ return ErrInvalidKey
+ }
+ _, err := w.Write(key)
+ return err
+}
+
+func writeValue(w io.Writer, value interface{}) error {
+ switch v := value.(type) {
+ case nil:
+ return writeBytesValue(w, null)
+ case string:
+ return writeStringValue(w, v, true)
+ case []byte:
+ return writeBytesValue(w, v)
+ case encoding.TextMarshaler:
+ vb, err := safeMarshal(v)
+ if err != nil {
+ return err
+ }
+ if vb == nil {
+ vb = null
+ }
+ return writeBytesValue(w, vb)
+ case error:
+ se, ok := safeError(v)
+ return writeStringValue(w, se, ok)
+ case fmt.Stringer:
+ ss, ok := safeString(v)
+ return writeStringValue(w, ss, ok)
+ default:
+ rvalue := reflect.ValueOf(value)
+ switch rvalue.Kind() {
+ case reflect.Array, reflect.Chan, reflect.Func, reflect.Map, reflect.Slice, reflect.Struct:
+ return ErrUnsupportedValueType
+ case reflect.Ptr:
+ if rvalue.IsNil() {
+ return writeBytesValue(w, null)
+ }
+ return writeValue(w, rvalue.Elem().Interface())
+ }
+ return writeStringValue(w, fmt.Sprint(v), true)
+ }
+}
+
+func needsQuotedValueRune(r rune) bool {
+ return r <= ' ' || r == '=' || r == '"' || r == utf8.RuneError
+}
+
+func writeStringValue(w io.Writer, value string, ok bool) error {
+ var err error
+ if ok && value == "null" {
+ _, err = io.WriteString(w, `"null"`)
+ } else if strings.IndexFunc(value, needsQuotedValueRune) != -1 {
+ _, err = writeQuotedString(w, value)
+ } else {
+ _, err = io.WriteString(w, value)
+ }
+ return err
+}
+
+func writeBytesValue(w io.Writer, value []byte) error {
+ var err error
+ if bytes.IndexFunc(value, needsQuotedValueRune) != -1 {
+ _, err = writeQuotedBytes(w, value)
+ } else {
+ _, err = w.Write(value)
+ }
+ return err
+}
+
+// EndRecord writes a newline character to the stream and resets the encoder
+// to the beginning of a new record.
+func (enc *Encoder) EndRecord() error {
+ _, err := enc.w.Write(newline)
+ if err == nil {
+ enc.needSep = false
+ }
+ return err
+}
+
+// Reset resets the encoder to the beginning of a new record.
+func (enc *Encoder) Reset() {
+ enc.needSep = false
+}
+
+func safeError(err error) (s string, ok bool) {
+ defer func() {
+ if panicVal := recover(); panicVal != nil {
+ if v := reflect.ValueOf(err); v.Kind() == reflect.Ptr && v.IsNil() {
+ s, ok = "null", false
+ } else {
+ panic(panicVal)
+ }
+ }
+ }()
+ s, ok = err.Error(), true
+ return
+}
+
+func safeString(str fmt.Stringer) (s string, ok bool) {
+ defer func() {
+ if panicVal := recover(); panicVal != nil {
+ if v := reflect.ValueOf(str); v.Kind() == reflect.Ptr && v.IsNil() {
+ s, ok = "null", false
+ } else {
+ panic(panicVal)
+ }
+ }
+ }()
+ s, ok = str.String(), true
+ return
+}
+
+func safeMarshal(tm encoding.TextMarshaler) (b []byte, err error) {
+ defer func() {
+ if panicVal := recover(); panicVal != nil {
+ if v := reflect.ValueOf(tm); v.Kind() == reflect.Ptr && v.IsNil() {
+ b, err = nil, nil
+ } else {
+ panic(panicVal)
+ }
+ }
+ }()
+ b, err = tm.MarshalText()
+ if err != nil {
+ return nil, &MarshalerError{
+ Type: reflect.TypeOf(tm),
+ Err: err,
+ }
+ }
+ return
+}
nihil fit ex nihilo