diff options
Diffstat (limited to 'vendor/github.com/kr/logfmt/decode.go')
-rw-r--r-- | vendor/github.com/kr/logfmt/decode.go | 184 |
1 files changed, 184 insertions, 0 deletions
diff --git a/vendor/github.com/kr/logfmt/decode.go b/vendor/github.com/kr/logfmt/decode.go new file mode 100644 index 0000000..1397fb7 --- /dev/null +++ b/vendor/github.com/kr/logfmt/decode.go @@ -0,0 +1,184 @@ +// Package implements the decoding of logfmt key-value pairs. +// +// Example logfmt message: +// +// foo=bar a=14 baz="hello kitty" cool%story=bro f %^asdf +// +// Example result in JSON: +// +// { "foo": "bar", "a": 14, "baz": "hello kitty", "cool%story": "bro", "f": true, "%^asdf": true } +// +// EBNFish: +// +// ident_byte = any byte greater than ' ', excluding '=' and '"' +// string_byte = any byte excluding '"' and '\' +// garbage = !ident_byte +// ident = ident_byte, { ident byte } +// key = ident +// value = ident | '"', { string_byte | '\', '"' }, '"' +// pair = key, '=', value | key, '=' | key +// message = { garbage, pair }, garbage +package logfmt + +import ( + "reflect" + "strconv" + "strings" + "time" +) + +// Handler is the interface implemented by objects that accept logfmt +// key-value pairs. HandleLogfmt must copy the logfmt data if it +// wishes to retain the data after returning. +type Handler interface { + HandleLogfmt(key, val []byte) error +} + +// The HandlerFunc type is an adapter to allow the use of ordinary functions as +// logfmt handlers. If f is a function with the appropriate signature, +// HandlerFunc(f) is a Handler object that calls f. +type HandlerFunc func(key, val []byte) error + +func (f HandlerFunc) HandleLogfmt(key, val []byte) error { + return f(key, val) +} + +// Unmarshal parses the logfmt encoding data and stores the result in the value +// pointed to by v. If v is an Handler, HandleLogfmt will be called for each +// key-value pair. +// +// If v is not a Handler, it will pass v to NewStructHandler and use the +// returned StructHandler for decoding. +func Unmarshal(data []byte, v interface{}) (err error) { + h, ok := v.(Handler) + if !ok { + h, err = NewStructHandler(v) + if err != nil { + return err + } + } + return gotoScanner(data, h) +} + +// StructHandler unmarshals logfmt into a struct. It matches incoming keys to +// the the struct's fields (either the struct field name or its tag, preferring +// an exact match but also accepting a case-insensitive match. +// +// Field types supported by StructHandler are: +// +// all numeric types (e.g. float32, int, etc.) +// []byte +// string +// bool - true if key is present, false otherwise (the value is ignored). +// time.Duration - uses time.ParseDuration +// +// If a field is a pointer to an above type, and a matching key is not present +// in the logfmt data, the pointer will be untouched. +// +// If v is not a pointer to an Handler or struct, Unmarshal will return an +// error. +type StructHandler struct { + rv reflect.Value +} + +func NewStructHandler(v interface{}) (Handler, error) { + rv := reflect.ValueOf(v) + if rv.Kind() != reflect.Ptr || rv.IsNil() { + return nil, &InvalidUnmarshalError{reflect.TypeOf(v)} + } + return &StructHandler{rv: rv}, nil +} + +func (h *StructHandler) HandleLogfmt(key, val []byte) error { + el := h.rv.Elem() + skey := string(key) + for i := 0; i < el.NumField(); i++ { + fv := el.Field(i) + ft := el.Type().Field(i) + switch { + case ft.Name == skey: + case ft.Tag.Get("logfmt") == skey: + case strings.EqualFold(ft.Name, skey): + default: + continue + } + if fv.Kind() == reflect.Ptr { + if fv.IsNil() { + t := fv.Type().Elem() + v := reflect.New(t) + fv.Set(v) + fv = v + } + fv = fv.Elem() + } + switch fv.Interface().(type) { + case time.Duration: + d, err := time.ParseDuration(string(val)) + if err != nil { + return &UnmarshalTypeError{string(val), fv.Type()} + } + fv.Set(reflect.ValueOf(d)) + case string: + fv.SetString(string(val)) + case []byte: + b := make([]byte, len(val)) + copy(b, val) + fv.SetBytes(b) + case bool: + fv.SetBool(true) + default: + switch { + case reflect.Int <= fv.Kind() && fv.Kind() <= reflect.Int64: + v, err := strconv.ParseInt(string(val), 10, 64) + if err != nil { + return err + } + fv.SetInt(v) + case reflect.Uint32 <= fv.Kind() && fv.Kind() <= reflect.Uint64: + v, err := strconv.ParseUint(string(val), 10, 64) + if err != nil { + return err + } + fv.SetUint(v) + case reflect.Float32 <= fv.Kind() && fv.Kind() <= reflect.Float64: + v, err := strconv.ParseFloat(string(val), 10) + if err != nil { + return err + } + fv.SetFloat(v) + default: + return &UnmarshalTypeError{string(val), fv.Type()} + } + } + + } + return nil +} + +// An InvalidUnmarshalError describes an invalid argument passed to Unmarshal. +// (The argument to Unmarshal must be a non-nil pointer.) +type InvalidUnmarshalError struct { + Type reflect.Type +} + +func (e *InvalidUnmarshalError) Error() string { + if e.Type == nil { + return "logfmt: Unmarshal(nil)" + } + + if e.Type.Kind() != reflect.Ptr { + return "logfmt: Unmarshal(non-pointer " + e.Type.String() + ")" + } + return "logfmt: Unmarshal(nil " + e.Type.String() + ")" +} + +// An UnmarshalTypeError describes a logfmt value that was +// not appropriate for a value of a specific Go type. +type UnmarshalTypeError struct { + Value string // the logfmt value + Type reflect.Type // type of Go value it could not be assigned to +} + +func (e *UnmarshalTypeError) Error() string { + return "logfmt: cannot unmarshal " + e.Value + " into Go value of type " + e.Type.String() +} |