Skip to content

Commit c9b68e6

Browse files
committed
func: Implement TextMarshaler for map keys
1 parent 058c4ca commit c9b68e6

File tree

2 files changed

+44
-6
lines changed

2 files changed

+44
-6
lines changed

funcr/funcr.go

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ package funcr
3636

3737
import (
3838
"bytes"
39+
"encoding"
3940
"fmt"
4041
"path/filepath"
4142
"reflect"
@@ -492,12 +493,24 @@ func (f Formatter) prettyWithFlags(value interface{}, flags uint32) string {
492493
if i > 0 {
493494
buf.WriteByte(',')
494495
}
495-
// prettyWithFlags will produce already-escaped values
496-
keystr := f.prettyWithFlags(it.Key().Interface(), 0)
497-
if t.Key().Kind() != reflect.String {
498-
// JSON only does string keys. Unlike Go's standard JSON, we'll
499-
// convert just about anything to a string.
496+
// If a map key supports TextMarshaler, use it.
497+
keystr := ""
498+
if m, ok := it.Key().Interface().(encoding.TextMarshaler); ok {
499+
txt, err := m.MarshalText()
500+
if err != nil {
501+
keystr = fmt.Sprintf("<error-MarshalText: %s>", err.Error())
502+
} else {
503+
keystr = string(txt)
504+
}
500505
keystr = prettyString(keystr)
506+
} else {
507+
// prettyWithFlags will produce already-escaped values
508+
keystr = f.prettyWithFlags(it.Key().Interface(), 0)
509+
if t.Key().Kind() != reflect.String {
510+
// JSON only does string keys. Unlike Go's standard JSON, we'll
511+
// convert just about anything to a string.
512+
keystr = prettyString(keystr)
513+
}
501514
}
502515
buf.WriteString(keystr)
503516
buf.WriteByte(':')

funcr/funcr_test.go

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,20 @@ func ptrstr(s string) *string {
3737
return &s
3838
}
3939

40+
// point implements encoding.TextMarshaler and can be used as a map key.
41+
type point struct{ x, y int }
42+
43+
func (p point) MarshalText() ([]byte, error) {
44+
return []byte(fmt.Sprintf("(%d, %d)", p.x, p.y)), nil
45+
}
46+
47+
// pointErr implements encoding.TextMarshaler but returns an error.
48+
type pointErr struct{ x, y int }
49+
50+
func (p pointErr) MarshalText() ([]byte, error) {
51+
return nil, fmt.Errorf("uh oh: %d, %d", p.x, p.y)
52+
}
53+
4054
// Logging this should result in the MarshalLog() value.
4155
type Tmarshaler string
4256

@@ -288,6 +302,17 @@ func TestPretty(t *testing.T) {
288302
},
289303
exp: `{"9.5":3}`,
290304
},
305+
{
306+
val: map[point]int{
307+
{x: 1, y: 2}: 3,
308+
},
309+
},
310+
{
311+
val: map[pointErr]int{
312+
{x: 1, y: 2}: 3,
313+
},
314+
exp: `{"<error-MarshalText: uh oh: 1, 2>":3}`,
315+
},
291316
{
292317
val: struct {
293318
X int `json:"x"`
@@ -496,7 +521,7 @@ func TestPretty(t *testing.T) {
496521
} else {
497522
jb, err := json.Marshal(tc.val)
498523
if err != nil {
499-
t.Fatalf("[%d]: unexpected error: %v", i, err)
524+
t.Fatalf("[%d]: unexpected error: %v\ngot: %q", i, err, ours)
500525
}
501526
want = string(jb)
502527
}

0 commit comments

Comments
 (0)