Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
test:
strategy:
matrix:
go-version: [1.24.x, 1.25.x]
go-version: [1.24.x, 1.25.x, 1.26.x]
os: [ubuntu-latest]
runs-on: ${{ matrix.os }}
timeout-minutes: 10
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/validate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
linters:
strategy:
matrix:
go-version: [1.25.x]
go-version: [1.26.x]
os: [ubuntu-latest]
runs-on: ${{ matrix.os }}
timeout-minutes: 10
Expand Down
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ ci: prepare
if [ `arch` == 'x86_64' ]; then \
sudo apt-get -y -q update; \
sudo apt-get -y -q install build-essential; \
wget -q https://github.com/tinygo-org/tinygo/releases/download/v0.39.0/tinygo_0.39.0_amd64.deb; \
sudo dpkg -i tinygo_0.39.0_amd64.deb; \
wget -q https://github.com/tinygo-org/tinygo/releases/download/v0.41.1/tinygo_0.41.1_amd64.deb; \
sudo dpkg -i tinygo_0.41.1_amd64.deb; \
export PATH=$$PATH:/usr/local/tinygo/bin; \
fi
go test -v ./... ./_generated
14 changes: 14 additions & 0 deletions _generated/custom_tags.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package _generated

//go:generate msgp -d "tags primary,fallback"

type CustomTags struct {
Field1 string `primary:"f1_primary"`
Field2 string `fallback:"f2_fallback"`
Field3 string `primary:"f3_primary" fallback:"f3_fallback_ignored"`
Field4 string `msg:"f4_msg"`
Field5 string `msgpack:"f5_msgpack"`
Field6 string `fallback:"f6_fallback" msg:"f6_msg_ignored"`
Field7 string `fallback:"f7_fallback" msgpack:"f7_msgpack_ignored"`
Field8 string `primary:"f8_primary" msg:"f8_msg_ignored" msgpack:"f8_msgpack_ignored"`
}
93 changes: 93 additions & 0 deletions _generated/custom_tags_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package _generated

import (
"bytes"
"encoding/json"
"reflect"
"testing"

"github.com/tinylib/msgp/msgp"
)

func TestCustomTags(t *testing.T) {
ts := CustomTags{
Field1: "v1",
Field2: "v2",
Field3: "v3",
Field4: "v4",
Field5: "v5",
Field6: "v6",
Field7: "v7",
Field8: "v8",
}
wantKeys := map[string]any{
"f1_primary": "v1",
"f2_fallback": "v2",
"f3_primary": "v3",
"f4_msg": "v4",
"f5_msgpack": "v5",
"f6_fallback": "v6",
"f7_fallback": "v7",
"f8_primary": "v8",
}

t.Run("EncodeDecode", func(t *testing.T) {
var b bytes.Buffer
if err := msgp.Encode(&b, &ts); err != nil {
t.Fatal(err)
}
var got CustomTags
if err := msgp.Decode(&b, &got); err != nil {
t.Fatal(err)
}
if got != ts {
t.Errorf("got %+v, want %+v", got, ts)
}
})

t.Run("MarshalUnmarshal", func(t *testing.T) {
buf, err := ts.MarshalMsg(nil)
if err != nil {
t.Fatal(err)
}
var got CustomTags
left, err := got.UnmarshalMsg(buf)
if err != nil {
t.Fatal(err)
}
if len(left) != 0 {
t.Errorf("%d bytes left after unmarshal", len(left))
}
if got != ts {
t.Errorf("got %+v, want %+v", got, ts)
}
})

t.Run("WireKeys", func(t *testing.T) {
buf, err := ts.MarshalMsg(nil)
if err != nil {
t.Fatal(err)
}
var jsonBuf bytes.Buffer
if _, err := msgp.UnmarshalAsJSON(&jsonBuf, buf); err != nil {
t.Fatal(err)
}
got := map[string]any{}
if err := json.Unmarshal(jsonBuf.Bytes(), &got); err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(got, wantKeys) {
t.Errorf("got %v, want %v", got, wantKeys)
}
})

t.Run("Msgsize", func(t *testing.T) {
buf, err := ts.MarshalMsg(nil)
if err != nil {
t.Fatal(err)
}
if est := ts.Msgsize(); est < len(buf) {
t.Errorf("Msgsize %d underestimates actual %d", est, len(buf))
}
})
}
23 changes: 20 additions & 3 deletions parse/directives.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ var directives = map[string]directive{
// and then add it to this list.
var earlyDirectives = map[string]directive{
"tag": tag,
"tags": tag,
"pointer": pointer,
"maps": maps,
}
Expand Down Expand Up @@ -237,12 +238,28 @@ func asvartuple(text []string, f *FileSet) error {
}

//msgp:tag {tagname}
//msgp:tags {tag1},{tag2},...
//
// The tag/tags directive accepts a comma-separated priority list; fields are
// read from the first tag that has a non-empty value, falling back to msg
// then msgpack if none of the listed tags match.
func tag(text []string, f *FileSet) error {
if len(text) != 2 {
if len(text) < 2 {
return nil
}
var names []string
for _, t := range text[1:] {
for _, n := range strings.Split(t, ",") {
Comment thread
klauspost marked this conversation as resolved.
Outdated
if n = strings.TrimSpace(n); n != "" {
names = append(names, n)
}
}
}
if len(names) == 0 {
return nil
}
f.tagName = strings.TrimSpace(text[1])
infof("using field tag %q\n", f.tagName)
f.tagNames = names
infof("using field tags %q\n", strings.Join(names, ","))
return nil
}

Expand Down
15 changes: 9 additions & 6 deletions parse/getast.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ type FileSet struct {
NoDuplicates bool // Reject duplicate keys for all types
NoDupTypes map[string]struct{} // Reject duplicate keys for specific types only

tagName string // tag to read field names from
pointerRcv bool // generate with pointer receivers.
tagNames []string // tags to read field names from, in priority order
pointerRcv bool // generate with pointer receivers.
}

// File parses a file at the relative path
Expand Down Expand Up @@ -527,15 +527,18 @@ func (fs *FileSet) getField(f *ast.Field) []gen.StructField {
var extension, flatten bool
// parse tag; otherwise field name is field tag
if f.Tag != nil {
st := reflect.StructTag(strings.Trim(f.Tag.Value, "`"))
var body string
if fs.tagName != "" {
body = reflect.StructTag(strings.Trim(f.Tag.Value, "`")).Get(fs.tagName)
for _, name := range fs.tagNames {
if body = st.Get(name); body != "" {
break
}
}
if body == "" {
Comment thread
klauspost marked this conversation as resolved.
Outdated
body = reflect.StructTag(strings.Trim(f.Tag.Value, "`")).Get("msg")
body = st.Get("msg")
}
if body == "" {
body = reflect.StructTag(strings.Trim(f.Tag.Value, "`")).Get("msgpack")
body = st.Get("msgpack")
}
tags := strings.Split(body, ",")
if len(tags) >= 2 {
Expand Down
Loading