-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathcontext.go
124 lines (103 loc) · 3.09 KB
/
context.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
package kv
import (
"context"
"fmt"
"time"
"github.com/jjeffery/kv/internal/pool"
)
// ctxKeyT is the type used for the context keys.
type ctxKeyT string
// ctxKey is the key used for storing key/value pairs
// in the context.
var ctxKey ctxKeyT = "kv"
// Context implements the context.Context interface,
// and can create a new context with key/value pairs
// attached to it.
type Context interface {
context.Context
// With returns a new context based on the existing context,
// but with with the key/value pairs attached.
With(keyvals ...interface{}) context.Context
// NewError returns a new error with the message text and
// the key/value pairs from the context attached.
NewError(text string) Error
// Wrap returns an error that wraps the existing error with
// the optional message text, and the key/value pairs from
// the context attached.
Wrap(err error, text ...string) Error
}
// contextT implements the Context interface.
type contextT struct {
ctx context.Context
}
// From creates a new context based on ctx. See the Context example.
func From(ctx context.Context) Context {
if c, ok := ctx.(Context); ok {
return c
}
return &contextT{ctx: ctx}
}
// Deadline implements the context.Context interface.
func (c *contextT) Deadline() (deadline time.Time, ok bool) {
return c.ctx.Deadline()
}
// Done implements the context.Context interface.
func (c *contextT) Done() <-chan struct{} {
return c.ctx.Done()
}
// Err implements the context.Context interface.
func (c *contextT) Err() error {
return c.ctx.Err()
}
// Value implements the context.Context interface.
func (c *contextT) Value(key interface{}) interface{} {
return c.ctx.Value(key)
}
// With returns a context.Context with the keyvals attached.
func (c *contextT) With(keyvals ...interface{}) context.Context {
return &contextT{ctx: newContext(c.ctx, keyvals)}
}
func newContext(ctx context.Context, keyvals []interface{}) context.Context {
if ctx == nil {
ctx = context.Background()
}
if len(keyvals) == 0 {
return ctx
}
keyvals = keyvals[:len(keyvals):len(keyvals)] // set capacity
keyvals = flattenFix(keyvals)
keyvals = append(keyvals, fromContext(ctx)...)
keyvals = keyvals[:len(keyvals):len(keyvals)] // set capacity
return context.WithValue(ctx, ctxKey, keyvals)
}
func fromContext(ctx context.Context) []interface{} {
var keyvals []interface{}
if ctx != nil {
keyvals, _ = ctx.Value(ctxKey).([]interface{})
}
return keyvals
}
func (c *contextT) NewError(text string) Error {
return newError(c.ctx, nil, text)
}
func (c *contextT) Wrap(err error, text ...string) Error {
return newError(c.ctx, err, text...)
}
func (c *contextT) String() string {
list := List(fromContext(c.ctx))
return list.String()
}
// Format implements the fmt.Formatter interface. If
// the context is printed with "%+v", then it prints
// using the String method of the wrapped context.
func (c *contextT) Format(f fmt.State, ch rune) {
if ch == 'v' && f.Flag('+') {
fmt.Fprint(f, c.ctx)
return
}
buf := pool.AllocBuffer()
list := List(fromContext(c.ctx))
list.writeToBuffer(buf)
f.Write(buf.Bytes())
pool.ReleaseBuffer(buf)
}