Skip to content

Commit dc17066

Browse files
authored
Merge pull request #67 from cdr/ammar
Add reflection encoder for lists
2 parents 20ab272 + 1dec25a commit dc17066

File tree

2 files changed

+46
-9
lines changed

2 files changed

+46
-9
lines changed

map.go

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,29 @@ import (
1313
// Map represents an ordered map of fields.
1414
type Map []Field
1515

16+
// SlogValue implements Value.
17+
func (m Map) SlogValue() interface{} {
18+
return ForceJSON(m)
19+
}
20+
1621
var _ json.Marshaler = Map(nil)
1722

1823
// MarshalJSON implements json.Marshaler.
1924
//
2025
// It is guaranteed to return a nil error.
21-
// Any error marshalling a field will
22-
// become the field's value.
26+
// Any error marshalling a field will become the field's value.
27+
//
28+
// Every field value is encoded with the following process:
29+
//
30+
// 1. slog.Value is handled to allow any type to replace its representation for logging.
31+
//
32+
// 2. xerrors.Formatter is handled.
33+
//
34+
// 3. error and fmt.Stringer are handled.
35+
//
36+
// 4. slices and arrays are handled to go through the encode function for every value.
37+
//
38+
// 5. json.Marshal is invoked as the default case.
2339
func (m Map) MarshalJSON() ([]byte, error) {
2440
b := &bytes.Buffer{}
2541
b.WriteByte('{')
@@ -55,14 +71,14 @@ func (v jsonVal) MarshalJSON() ([]byte, error) {
5571
return json.Marshal(v.v)
5672
}
5773

58-
func marshalArray(a []interface{}) []byte {
74+
func marshalList(rv reflect.Value) []byte {
5975
b := &bytes.Buffer{}
6076
b.WriteByte('[')
61-
for i, v := range a {
77+
for i := 0; i < rv.Len(); i++ {
6278
b.WriteByte('\n')
63-
b.Write(encode(v))
79+
b.Write(encode(rv.Index(i).Interface()))
6480

65-
if i < len(a)-1 {
81+
if i < rv.Len()-1 {
6682
b.WriteByte(',')
6783
}
6884
}
@@ -75,13 +91,24 @@ func encode(v interface{}) []byte {
7591
switch v := v.(type) {
7692
case Value:
7793
return encode(v.SlogValue())
78-
case []interface{}:
79-
return marshalArray(v)
8094
case xerrors.Formatter:
8195
return encode(errorChain(v))
8296
case error, fmt.Stringer:
8397
return encode(fmt.Sprint(v))
8498
default:
99+
rv := reflect.Indirect(reflect.ValueOf(v))
100+
if rv.IsValid() {
101+
switch rv.Type().Kind() {
102+
case reflect.Slice:
103+
if rv.IsNil() {
104+
break
105+
}
106+
fallthrough
107+
case reflect.Array:
108+
return marshalList(rv)
109+
}
110+
}
111+
85112
b, err := json.Marshal(v)
86113
if err != nil {
87114
return encode(M(

map_test.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ func TestMap(t *testing.T) {
9393
{
9494
"msg": "failed to marshal to JSON",
9595
"fun": "cdr.dev/slog.encode",
96-
"loc": "`+mapTestFile+`:88"
96+
"loc": "`+mapTestFile+`:115"
9797
},
9898
"json: unsupported type: func(*testing.T, string) string"
9999
],
@@ -164,6 +164,16 @@ func TestMap(t *testing.T) {
164164
"error": "xdxd"
165165
}`)
166166
})
167+
168+
t.Run("nilSlice", func(t *testing.T) {
169+
t.Parallel()
170+
171+
test(t, slog.M(
172+
slog.F("slice", []string(nil)),
173+
), `{
174+
"slice": null
175+
}`)
176+
})
167177
}
168178

169179
type meow struct {

0 commit comments

Comments
 (0)