From 9b5b3b2ef0909f9c319f30c674cba04effc5e849 Mon Sep 17 00:00:00 2001 From: Tom Date: Sat, 26 Nov 2022 22:25:27 +0400 Subject: [PATCH] feat: support toml (#4) --- README.md | 6 +- go.mod | 1 + go.sum | 2 + musttag.go | 15 ++++ musttag_test.go | 5 +- testdata/src/examples/examples.go | 10 +++ .../src/github.com/BurntSushi/toml/toml.go | 25 ++++++ testdata/src/gopkg.in/yaml.v3/yaml.go | 2 +- testdata/src/tests/tests.go | 88 +++++++++++++++++-- 9 files changed, 140 insertions(+), 14 deletions(-) create mode 100644 testdata/src/github.com/BurntSushi/toml/toml.go diff --git a/README.md b/README.md index 7b82f80..4e9bea3 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,7 @@ The rational from [Uber Style Guide][1]: * `encoding/json` support * `encoding/xml` support * `gopkg.in/yaml.v3` support +* `github.com/BurntSushi/toml` support ## 📦 Install @@ -46,6 +47,10 @@ go install github.com/junk1tm/musttag/cmd/musttag ## 📋 Usage +```shell +musttag ./... +``` + With `go vet`: ```shell @@ -54,7 +59,6 @@ go vet -vettool=$(which musttag) ./... ## 📅 Roadmap -* Support `toml` * Support `mapstructure` * Support custom tags via config diff --git a/go.mod b/go.mod index f5a7487..ed187ae 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/junk1tm/musttag go 1.18 require ( + github.com/BurntSushi/toml v1.2.1 golang.org/x/tools v0.3.0 gopkg.in/yaml.v3 v3.0.1 ) diff --git a/go.sum b/go.sum index 361394b..33067ec 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak= +github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= golang.org/x/mod v0.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA= golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= diff --git a/musttag.go b/musttag.go index bd2e848..20f959c 100644 --- a/musttag.go +++ b/musttag.go @@ -13,6 +13,7 @@ import ( // we need these dependencies for the tests in testdata to compile. // `go mod tidy` will remove them from go.mod if we don't import them here. + _ "github.com/BurntSushi/toml" _ "gopkg.in/yaml.v3" ) @@ -84,6 +85,7 @@ func tagAndExpr(pass *analysis.Pass, call *ast.CallExpr) (string, ast.Expr, bool jsonTag = "json" xmlTag = "xml" yamlTag = "yaml" + tomlTag = "toml" ) fn := typeutil.StaticCallee(pass.TypesInfo, call) @@ -99,6 +101,7 @@ func tagAndExpr(pass *analysis.Pass, call *ast.CallExpr) (string, ast.Expr, bool return jsonTag, call.Args[0], true case "encoding/json.Unmarshal": return jsonTag, call.Args[1], true + case "encoding/xml.Marshal", "encoding/xml.MarshalIndent", "(*encoding/xml.Encoder).Encode", @@ -108,12 +111,24 @@ func tagAndExpr(pass *analysis.Pass, call *ast.CallExpr) (string, ast.Expr, bool return xmlTag, call.Args[0], true case "encoding/xml.Unmarshal": return xmlTag, call.Args[1], true + case "gopkg.in/yaml.v3.Marshal", "(*gopkg.in/yaml.v3.Encoder).Encode", "(*gopkg.in/yaml.v3.Decoder).Decode": return yamlTag, call.Args[0], true case "gopkg.in/yaml.v3.Unmarshal": return yamlTag, call.Args[1], true + + case "(*github.com/BurntSushi/toml.Encoder).Encode", + "(*github.com/BurntSushi/toml.Decoder).Decode": + return tomlTag, call.Args[0], true + case "github.com/BurntSushi/toml.Unmarshal", + "github.com/BurntSushi/toml.Decode", + "github.com/BurntSushi/toml.DecodeFile": + return tomlTag, call.Args[1], true + case "github.com/BurntSushi/toml.DecodeFS": + return tomlTag, call.Args[2], true + default: return "", nil, false } diff --git a/musttag_test.go b/musttag_test.go index 4ee1573..fcf6c10 100644 --- a/musttag_test.go +++ b/musttag_test.go @@ -3,6 +3,7 @@ package musttag import ( "go/ast" "go/token" + "path" "strings" "testing" @@ -44,7 +45,5 @@ func shortName(name string) string { name = strings.ReplaceAll(name, "*", "") name = strings.ReplaceAll(name, "(", "") name = strings.ReplaceAll(name, ")", "") - name = strings.TrimPrefix(name, "encoding/") - name = strings.TrimPrefix(name, "gopkg.in/") - return name + return path.Base(name) } diff --git a/testdata/src/examples/examples.go b/testdata/src/examples/examples.go index 1a2f1e8..ef76520 100644 --- a/testdata/src/examples/examples.go +++ b/testdata/src/examples/examples.go @@ -4,6 +4,7 @@ import ( "encoding/json" "encoding/xml" + "github.com/BurntSushi/toml" "gopkg.in/yaml.v3" ) @@ -35,3 +36,12 @@ func exampleYAML() { yaml.Marshal(user) yaml.Unmarshal(nil, &user) } + +func exampleTOML() { + var user struct { // want `\Qexported fields should be annotated with the "toml" tag` + Name string + Email string `toml:"email"` + } + toml.Decode("", &user) + toml.Unmarshal(nil, &user) +} diff --git a/testdata/src/github.com/BurntSushi/toml/toml.go b/testdata/src/github.com/BurntSushi/toml/toml.go new file mode 100644 index 0000000..e972579 --- /dev/null +++ b/testdata/src/github.com/BurntSushi/toml/toml.go @@ -0,0 +1,25 @@ +// Package toml provides stubs for github.com/BurntSushi/toml. +package toml + +import ( + "io" + "io/fs" +) + +func Unmarshal(_ []byte, _ any) error { return nil } + +type MetaData struct{} + +func Decode(_ string, _ any) (MetaData, error) { return MetaData{}, nil } +func DecodeFS(_ fs.FS, _ string, _ any) (MetaData, error) { return MetaData{}, nil } +func DecodeFile(_ string, _ any) (MetaData, error) { return MetaData{}, nil } + +type Encoder struct{} + +func NewEncoder(_ io.Writer) *Encoder { return nil } +func (*Encoder) Encode(_ any) error { return nil } + +type Decoder struct{} + +func NewDecoder(_ io.Reader) *Decoder { return nil } +func (*Decoder) Decode(_ any) error { return nil } diff --git a/testdata/src/gopkg.in/yaml.v3/yaml.go b/testdata/src/gopkg.in/yaml.v3/yaml.go index aa043a0..88b4aa0 100644 --- a/testdata/src/gopkg.in/yaml.v3/yaml.go +++ b/testdata/src/gopkg.in/yaml.v3/yaml.go @@ -8,7 +8,7 @@ func Unmarshal(_ []byte, _ any) error { return nil } type Encoder struct{} -func NewEncoder(w io.Writer) *Encoder { return nil } +func NewEncoder(_ io.Writer) *Encoder { return nil } func (*Encoder) Encode(_ any) error { return nil } type Decoder struct{} diff --git a/testdata/src/tests/tests.go b/testdata/src/tests/tests.go index d30be28..c3a7a87 100644 --- a/testdata/src/tests/tests.go +++ b/testdata/src/tests/tests.go @@ -4,6 +4,7 @@ import ( "encoding/json" "encoding/xml" + "github.com/BurntSushi/toml" "gopkg.in/yaml.v3" ) @@ -28,7 +29,14 @@ func namedType() { `\Qyaml.v3.Marshal` `\Qyaml.v3.Unmarshal` `\Qyaml.v3.Encoder.Encode` - `\Qyaml.v3.Decoder.Decode` */ + `\Qyaml.v3.Decoder.Decode` + + `\Qtoml.Unmarshal` + `\Qtoml.Decode` + `\Qtoml.DecodeFS` + `\Qtoml.DecodeFile` + `\Qtoml.Encoder.Encode` + `\Qtoml.Decoder.Decode` */ NoTag int } var x X @@ -51,6 +59,13 @@ func namedType() { yaml.Unmarshal(nil, &x) yaml.NewEncoder(nil).Encode(X{}) yaml.NewDecoder(nil).Decode(&X{}) + + toml.Unmarshal(nil, &x) + toml.Decode("", &x) + toml.DecodeFS(nil, "", &x) + toml.DecodeFile("", &x) + toml.NewEncoder(nil).Encode(X{}) + toml.NewDecoder(nil).Decode(&X{}) } func nestedNamedType() { @@ -72,11 +87,18 @@ func nestedNamedType() { `\Qyaml.v3.Marshal` `\Qyaml.v3.Unmarshal` `\Qyaml.v3.Encoder.Encode` - `\Qyaml.v3.Decoder.Decode` */ + `\Qyaml.v3.Decoder.Decode` + + `\Qtoml.Unmarshal` + `\Qtoml.Decode` + `\Qtoml.DecodeFS` + `\Qtoml.DecodeFile` + `\Qtoml.Encoder.Encode` + `\Qtoml.Decoder.Decode` */ NoTag int } type X struct { - Y Y `json:"y" xml:"Y" yaml:"y"` + Y Y `json:"y" xml:"y" yaml:"y" toml:"y"` } var x X @@ -98,6 +120,13 @@ func nestedNamedType() { yaml.Unmarshal(nil, &x) yaml.NewEncoder(nil).Encode(X{}) yaml.NewDecoder(nil).Decode(&X{}) + + toml.Unmarshal(nil, &x) + toml.Decode("", &x) + toml.DecodeFS(nil, "", &x) + toml.DecodeFile("", &x) + toml.NewEncoder(nil).Encode(X{}) + toml.NewDecoder(nil).Decode(&X{}) } func anonymousType() { @@ -111,7 +140,12 @@ func anonymousType() { `\Qxml.Unmarshal` `\Qyaml.v3.Marshal` - `\Qyaml.v3.Unmarshal` */ + `\Qyaml.v3.Unmarshal` + + `\Qtoml.Unmarshal` + `\Qtoml.Decode` + `\Qtoml.DecodeFS` + `\Qtoml.DecodeFile` */ NoTag int } @@ -133,6 +167,13 @@ func anonymousType() { yaml.Unmarshal(nil, &x) yaml.NewEncoder(nil).Encode(struct{ NoTag int }{}) // want `\Qyaml.v3.Encoder.Encode` yaml.NewDecoder(nil).Decode(&struct{ NoTag int }{}) // want `\Qyaml.v3.Decoder.Decode` + + toml.Unmarshal(nil, &x) + toml.Decode("", &x) + toml.DecodeFS(nil, "", &x) + toml.DecodeFile("", &x) + toml.NewEncoder(nil).Encode(struct{ NoTag int }{}) // want `\Qtoml.Encoder.Encode` + toml.NewDecoder(nil).Decode(&struct{ NoTag int }{}) // want `\Qtoml.Decoder.Decode` } func nestedAnonymousType() { @@ -146,8 +187,13 @@ func nestedAnonymousType() { `\Qxml.Unmarshal` `\Qyaml.v3.Marshal` - `\Qyaml.v3.Unmarshal` */ - Y *struct{ NoTag int } `json:"y"` + `\Qyaml.v3.Unmarshal` + + `\Qtoml.Unmarshal` + `\Qtoml.Decode` + `\Qtoml.DecodeFS` + `\Qtoml.DecodeFile` */ + Y *struct{ NoTag int } `json:"y" xml:"y" yaml:"y" toml:"y"` } json.Marshal(x) @@ -168,14 +214,21 @@ func nestedAnonymousType() { yaml.Unmarshal(nil, &x) yaml.NewEncoder(nil).Encode(struct{ Y struct{ NoTag int } }{}) // want `\Qyaml.v3.Encoder.Encode` yaml.NewDecoder(nil).Decode(&struct{ Y struct{ NoTag int } }{}) // want `\Qyaml.v3.Decoder.Decode` + + toml.Unmarshal(nil, &x) + toml.Decode("", &x) + toml.DecodeFS(nil, "", &x) + toml.DecodeFile("", &x) + toml.NewEncoder(nil).Encode(struct{ Y struct{ NoTag int } }{}) // want `\Qtoml.Encoder.Encode` + toml.NewDecoder(nil).Decode(&struct{ Y struct{ NoTag int } }{}) // want `\Qtoml.Decoder.Decode` } // all good, nothing to report. func typeWithAllTags() { var x struct { - Y int `json:"y" xml:"Y" yaml:"y"` - Z int `json:"z" xml:"Z" yaml:"z"` - Nested struct{} `json:"nested" xml:"Nested" yaml:"nested"` + Y int `json:"y" xml:"y" yaml:"y" toml:"y"` + Z int `json:"z" xml:"z" yaml:"z" toml:"z"` + Nested struct{} `json:"nested" xml:"nested" yaml:"nested" toml:"nested"` private int } @@ -197,6 +250,13 @@ func typeWithAllTags() { yaml.Unmarshal(nil, &x) yaml.NewEncoder(nil).Encode(x) yaml.NewDecoder(nil).Decode(&x) + + toml.Unmarshal(nil, &x) + toml.Decode("", &x) + toml.DecodeFS(nil, "", &x) + toml.DecodeFile("", &x) + toml.NewEncoder(nil).Encode(x) + toml.NewDecoder(nil).Decode(&x) } // non-static calls should be ignored. @@ -213,6 +273,9 @@ func nonStaticCalls() { marshalYAML := yaml.Marshal marshalYAML(x) + + unmarshalTOML := toml.Unmarshal + unmarshalTOML(nil, &x) } // non-struct argument calls should be ignored. @@ -235,4 +298,11 @@ func nonStructArgument() { yaml.Unmarshal(nil, &[]int{}) yaml.NewEncoder(nil).Encode(map[int]int{}) yaml.NewDecoder(nil).Decode(&map[int]int{}) + + toml.Unmarshal(nil, &[]int{}) + toml.Decode("", &[]int{}) + toml.DecodeFS(nil, "", &[]int{}) + toml.DecodeFile("", &[]int{}) + toml.NewEncoder(nil).Encode(map[int]int{}) + toml.NewDecoder(nil).Decode(&map[int]int{}) }