From 1e893e1cc74005a8baff5f37c73ff88e4adf48ad Mon Sep 17 00:00:00 2001 From: Gabriel Handford Date: Fri, 21 Aug 2020 10:57:15 -0700 Subject: [PATCH] json: Use TextMarshaler interface --- encoding/encode.go | 4 ++++ json/marshal.go | 57 +++++++++++++++++++++++++------------------- json/marshal_test.go | 20 ++++++++-------- statement.go | 20 ++++++++-------- user/user.go | 14 +++++------ 5 files changed, 64 insertions(+), 51 deletions(-) diff --git a/encoding/encode.go b/encoding/encode.go index 271a5ad..0bd526a 100644 --- a/encoding/encode.go +++ b/encoding/encode.go @@ -2,6 +2,7 @@ package encoding import ( + stdencoding "encoding" "encoding/base32" "encoding/base64" "encoding/hex" @@ -11,6 +12,9 @@ import ( "github.com/pkg/errors" ) +// TextMarshaler alias for encoding.TextMarshaler. +type TextMarshaler = stdencoding.TextMarshaler + // Encoding is an encoding for bytes to and from a string type Encoding string diff --git a/json/marshal.go b/json/marshal.go index 79c1d65..3ddf160 100644 --- a/json/marshal.go +++ b/json/marshal.go @@ -2,12 +2,13 @@ package json import ( + "bytes" "encoding/json" "regexp" "strconv" - "strings" "github.com/keys-pub/keys/encoding" + "github.com/pkg/errors" ) @@ -21,55 +22,63 @@ type intEntry struct { value int } -// Value to string. -type Value interface { - Marshal() (string, error) -} - -// NewString ... -func NewString(key string, value string) Value { +// String ... +func String(key string, value string) encoding.TextMarshaler { return stringEntry{key: key, value: value} } -// NewInt ... -func NewInt(key string, value int) Value { +// Int ... +func Int(key string, value int) encoding.TextMarshaler { return intEntry{key: key, value: value} } var isAlphaNumericDot = regexp.MustCompile(`^[a-zA-Z0-9.]+$`).MatchString var needsEscape = regexp.MustCompile(`^[\"]+$`).MatchString -func (e stringEntry) Marshal() (string, error) { +func (e stringEntry) MarshalText() ([]byte, error) { if !isAlphaNumericDot(e.key) { - return "", errors.Errorf("invalid character in key") + return nil, errors.Errorf("invalid character in key") } if needsEscape(e.value) { - return "", errors.Errorf("invalid character in value") + return nil, errors.Errorf("invalid character in value") } if !encoding.IsASCII([]byte(e.value)) { - return "", errors.Errorf("invalid character in value") + return nil, errors.Errorf("invalid character in value") } - return `"` + e.key + `":"` + e.value + `"`, nil + return []byte(`"` + e.key + `":"` + e.value + `"`), nil } -func (e intEntry) Marshal() (string, error) { +func (e intEntry) MarshalText() ([]byte, error) { if !isAlphaNumericDot(e.key) { - return "", errors.Errorf("invalid character in key") + return nil, errors.Errorf("invalid character in key") } - return `"` + e.key + `":` + strconv.Itoa(e.value), nil + + b := []byte{} + b = append(b, '"') + b = append(b, []byte(e.key)...) + b = append(b, '"', ':') + b = append(b, []byte(strconv.Itoa(e.value))...) + + return b, nil } // Marshal values. -func Marshal(es []Value) ([]byte, error) { - out := make([]string, 0, len(es)) - for _, e := range es { - s, err := e.Marshal() +func Marshal(vals ...encoding.TextMarshaler) ([]byte, error) { + out := make([][]byte, 0, len(vals)) + for _, val := range vals { + b, err := val.MarshalText() if err != nil { return nil, err } - out = append(out, s) + out = append(out, b) } - return []byte("{" + strings.Join(out, ",") + "}"), nil + + b := []byte{} + b = append(b, '{') + b = append(b, bytes.Join(out, []byte{','})...) + b = append(b, '}') + + return b, nil } // Unmarshal bytes. diff --git a/json/marshal_test.go b/json/marshal_test.go index b7693ca..5738e53 100644 --- a/json/marshal_test.go +++ b/json/marshal_test.go @@ -8,19 +8,19 @@ import ( ) func TestMarshal(t *testing.T) { - b, err := json.Marshal([]json.Value{ - json.NewString("key1", "val1"), - json.NewInt("key2", 2), - }) + b, err := json.Marshal( + json.String("key1", "val1"), + json.Int("key2", 2), + ) require.NoError(t, err) require.Equal(t, `{"key1":"val1","key2":2}`, string(b)) - _, err = json.Marshal([]json.Value{ - json.NewString(`"`, ""), - }) + _, err = json.Marshal( + json.String(`"`, ""), + ) require.EqualError(t, err, "invalid character in key") - _, err = json.Marshal([]json.Value{ - json.NewString("key1", `"`), - }) + _, err = json.Marshal( + json.String("key1", `"`), + ) require.EqualError(t, err, "invalid character in value") } diff --git a/statement.go b/statement.go index cfeec76..c1a5a0f 100644 --- a/statement.go +++ b/statement.go @@ -210,30 +210,30 @@ func statementBytesToSign(st *Statement) ([]byte, error) { } func statementBytes(st *Statement, sig []byte) ([]byte, error) { - mes := []json.Value{ - json.NewString(".sig", encoding.MustEncode(sig, encoding.Base64)), + mes := []encoding.TextMarshaler{ + json.String(".sig", encoding.MustEncode(sig, encoding.Base64)), } if len(st.Data) != 0 { - mes = append(mes, json.NewString("data", encoding.MustEncode(st.Data, encoding.Base64))) + mes = append(mes, json.String("data", encoding.MustEncode(st.Data, encoding.Base64))) } - mes = append(mes, json.NewString("kid", st.KID.String())) + mes = append(mes, json.String("kid", st.KID.String())) if len(st.Prev) != 0 { - mes = append(mes, json.NewString("prev", encoding.MustEncode(st.Prev, encoding.Base64))) + mes = append(mes, json.String("prev", encoding.MustEncode(st.Prev, encoding.Base64))) } if st.Revoke != 0 { - mes = append(mes, json.NewInt("revoke", st.Revoke)) + mes = append(mes, json.Int("revoke", st.Revoke)) } if st.Seq != 0 { - mes = append(mes, json.NewInt("seq", st.Seq)) + mes = append(mes, json.Int("seq", st.Seq)) } if !st.Timestamp.IsZero() { - mes = append(mes, json.NewInt("ts", int(tsutil.Millis(st.Timestamp)))) + mes = append(mes, json.Int("ts", int(tsutil.Millis(st.Timestamp)))) } if st.Type != "" { - mes = append(mes, json.NewString("type", st.Type)) + mes = append(mes, json.String("type", st.Type)) } - return json.Marshal(mes) + return json.Marshal(mes...) } // unmarshalJSON returns a Statement from JSON bytes. diff --git a/user/user.go b/user/user.go index 0cb946a..623cb79 100644 --- a/user/user.go +++ b/user/user.go @@ -45,19 +45,19 @@ func (u User) MarshalJSON() ([]byte, error) { // Bytes is a serialized User. func (u User) Bytes() ([]byte, error) { - mes := []json.Value{} + mes := []encoding.TextMarshaler{} - mes = append(mes, json.NewString("k", u.KID.String())) - mes = append(mes, json.NewString("n", u.Name)) + mes = append(mes, json.String("k", u.KID.String())) + mes = append(mes, json.String("n", u.Name)) if u.Seq != 0 { - mes = append(mes, json.NewInt("sq", u.Seq)) + mes = append(mes, json.Int("sq", u.Seq)) } - mes = append(mes, json.NewString("sr", u.Service)) + mes = append(mes, json.String("sr", u.Service)) if u.URL != "" { - mes = append(mes, json.NewString("u", u.URL)) + mes = append(mes, json.String("u", u.URL)) } - return json.Marshal(mes) + return json.Marshal(mes...) } // Status is the status of the user statement.