-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathcontext.go
154 lines (124 loc) · 2.97 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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
package karma
import (
"encoding/json"
"fmt"
)
// Context is a element of key-value linked list of message contexts.
type Context struct {
KeyValue
Next *Context
}
type KeyValue struct {
Key string `json:"key"`
Value interface{} `json:"value"`
}
// Context adds new key-value context pair to current context list and return
// new context list.
func (context *Context) Describe(
key string,
value interface{},
) *Context {
if context == nil {
return &Context{
KeyValue: KeyValue{
Key: key,
Value: value,
},
}
}
head := *context
pointer := &head
for pointer.Next != nil {
copy := *pointer.Next
pointer.Next = ©
pointer = pointer.Next
}
pointer.Next = &Context{
KeyValue: KeyValue{
Key: key,
Value: value,
},
}
return &head
}
// Format produces context-rich hierarchical message, which will include all
// previously declared context key-value pairs.
func (context *Context) Format(
reason Reason,
message string,
args ...interface{},
) Karma {
return Karma{
Message: fmt.Sprintf(message, args...),
Reason: reason,
Context: context,
}
}
// Reason adds current context to the specified message. If message is not
// hierarchical, it will be converted to such.
func (context *Context) Reason(reason Reason) Karma {
if previous, ok := reason.(Karma); ok {
context.Walk(func(key string, value interface{}) {
previous.Context = previous.Context.Describe(key, value)
})
return previous
} else {
return Karma{
Reason: reason,
Context: context,
}
}
}
// Walk iterates over all key-value context pairs and calls specified
// callback for each.
func (context *Context) Walk(callback func(string, interface{})) {
if context == nil {
return
}
if context.Key != "" || context.Value != nil {
callback(context.Key, context.Value)
}
if context.Next != nil {
context.Next.Walk(callback)
}
}
// GetKeyValuePairs returns slice of key-value context pairs, which will
// be always even, each even index is key and each odd index is value.
func (context *Context) GetKeyValuePairs() []interface{} {
pairs := []interface{}{}
context.Walk(func(name string, value interface{}) {
pairs = append(pairs, name, value)
})
return pairs
}
// GetKeyValues returns context as slice of key-values.
func (context *Context) GetKeyValues() []KeyValue {
result := []KeyValue{}
context.Walk(func(name string, value interface{}) {
result = append(result, KeyValue{name, value})
})
return result
}
func (context *Context) MarshalJSON() ([]byte, error) {
linear := []interface{}{}
context.Walk(func(key string, value interface{}) {
linear = append(linear, KeyValue{
key,
value,
})
})
return json.Marshal(linear)
}
func (context *Context) UnmarshalJSON(data []byte) error {
var container []KeyValue
err := json.Unmarshal(data, &container)
if err != nil {
return err
}
var result *Context
for _, item := range container {
result = result.Describe(item.Key, item.Value)
}
*context = *result
return nil
}