From d632eb86f4693d2f54cc8b997a389f9294d222c1 Mon Sep 17 00:00:00 2001 From: Tom <73077675+tmzane@users.noreply.github.com> Date: Sat, 27 May 2023 00:19:21 +0300 Subject: [PATCH 1/2] feat: support sqlx --- builtins.go | 27 +++++++++++++++++++++ musttag_test.go | 38 +++++++++++++++++++++++++++++ testdata/builtins.go | 58 +++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 122 insertions(+), 1 deletion(-) diff --git a/builtins.go b/builtins.go index 50573f8..66914fa 100644 --- a/builtins.go +++ b/builtins.go @@ -37,4 +37,31 @@ var builtins = []Func{ {Name: "github.com/mitchellh/mapstructure.DecodeMetadata", Tag: "mapstructure", ArgPos: 1}, {Name: "github.com/mitchellh/mapstructure.WeakDecode", Tag: "mapstructure", ArgPos: 1}, {Name: "github.com/mitchellh/mapstructure.WeakDecodeMetadata", Tag: "mapstructure", ArgPos: 1}, + + // https://github.com/jmoiron/sqlx + {Name: "github.com/jmoiron/sqlx.Get", Tag: "db", ArgPos: 1}, + {Name: "github.com/jmoiron/sqlx.GetContext", Tag: "db", ArgPos: 2}, + {Name: "github.com/jmoiron/sqlx.Select", Tag: "db", ArgPos: 1}, + {Name: "github.com/jmoiron/sqlx.SelectContext", Tag: "db", ArgPos: 2}, + {Name: "github.com/jmoiron/sqlx.StructScan", Tag: "db", ArgPos: 1}, + {Name: "(*github.com/jmoiron/sqlx.Conn).GetContext", Tag: "db", ArgPos: 1}, + {Name: "(*github.com/jmoiron/sqlx.Conn).SelectContext", Tag: "db", ArgPos: 1}, + {Name: "(*github.com/jmoiron/sqlx.DB).Get", Tag: "db", ArgPos: 0}, + {Name: "(*github.com/jmoiron/sqlx.DB).GetContext", Tag: "db", ArgPos: 1}, + {Name: "(*github.com/jmoiron/sqlx.DB).Select", Tag: "db", ArgPos: 0}, + {Name: "(*github.com/jmoiron/sqlx.DB).SelectContext", Tag: "db", ArgPos: 1}, + {Name: "(*github.com/jmoiron/sqlx.NamedStmt).Get", Tag: "db", ArgPos: 0}, + {Name: "(*github.com/jmoiron/sqlx.NamedStmt).GetContext", Tag: "db", ArgPos: 1}, + {Name: "(*github.com/jmoiron/sqlx.NamedStmt).Select", Tag: "db", ArgPos: 0}, + {Name: "(*github.com/jmoiron/sqlx.NamedStmt).SelectContext", Tag: "db", ArgPos: 1}, + {Name: "(*github.com/jmoiron/sqlx.Row).StructScan", Tag: "db", ArgPos: 0}, + {Name: "(*github.com/jmoiron/sqlx.Rows).StructScan", Tag: "db", ArgPos: 0}, + {Name: "(*github.com/jmoiron/sqlx.Stmt).Get", Tag: "db", ArgPos: 0}, + {Name: "(*github.com/jmoiron/sqlx.Stmt).GetContext", Tag: "db", ArgPos: 1}, + {Name: "(*github.com/jmoiron/sqlx.Stmt).Select", Tag: "db", ArgPos: 0}, + {Name: "(*github.com/jmoiron/sqlx.Stmt).SelectContext", Tag: "db", ArgPos: 1}, + {Name: "(*github.com/jmoiron/sqlx.Tx).Get", Tag: "db", ArgPos: 0}, + {Name: "(*github.com/jmoiron/sqlx.Tx).GetContext", Tag: "db", ArgPos: 1}, + {Name: "(*github.com/jmoiron/sqlx.Tx).Select", Tag: "db", ArgPos: 0}, + {Name: "(*github.com/jmoiron/sqlx.Tx).SelectContext", Tag: "db", ArgPos: 1}, } diff --git a/musttag_test.go b/musttag_test.go index 5babb58..d503709 100644 --- a/musttag_test.go +++ b/musttag_test.go @@ -143,6 +143,44 @@ func DecodeMetadata(_, _ any, _ *Metadata) error { return nil } func WeakDecode(_, _ any) error { return nil } func WeakDecodeMetadata(_, _ any, _ *Metadata) error { return nil }`, + "github.com/jmoiron/sqlx/sqlx.go": `package sqlx +import "context" +type Queryer interface{} +type QueryerContext interface{} +type rowsi interface{} +func Get(Queryer, any, string, ...any) error { return nil } +func GetContext(context.Context, QueryerContext, any, string, ...any) error { return nil } +func Select(Queryer, any, string, ...any) error { return nil } +func SelectContext(context.Context, QueryerContext, any, string, ...any) error { return nil } +func StructScan(rowsi, any) error { return nil } +type Conn struct{} +func (*Conn) GetContext(context.Context, any, string, ...any) error { return nil } +func (*Conn) SelectContext(context.Context, any, string, ...any) error { return nil } +type DB struct{} +func (*DB) Get(any, string, ...any) error { return nil } +func (*DB) GetContext(context.Context, any, string, ...any) error { return nil } +func (*DB) Select(any, string, ...any) error { return nil } +func (*DB) SelectContext(context.Context, any, string, ...any) error { return nil } +type NamedStmt struct{} +func (n *NamedStmt) Get(any, any) error { return nil } +func (n *NamedStmt) GetContext(context.Context, any, any) error { return nil } +func (n *NamedStmt) Select(any, any) error { return nil } +func (n *NamedStmt) SelectContext(context.Context, any, any) error { return nil } +type Row struct{} +func (*Row) StructScan(any) error { return nil } +type Rows struct{} +func (*Rows) StructScan(any) error { return nil } +type Stmt struct{} +func (*Stmt) Get(any, ...any) error { return nil } +func (*Stmt) GetContext(context.Context, any, ...any) error { return nil } +func (*Stmt) Select(any, ...any) error { return nil } +func (*Stmt) SelectContext(context.Context, any, ...any) error { return nil } +type Tx struct{} +func (*Tx) Get(any, string, ...any) error { return nil } +func (*Tx) GetContext(context.Context, any, string, ...any) error { return nil } +func (*Tx) Select(any, string, ...any) error { return nil } +func (*Tx) SelectContext(context.Context, any, string, ...any) error { return nil }`, + "example.com/custom/custom.go": `package custom func Marshal(_ any) ([]byte, error) { return nil, nil } func Unmarshal(_ []byte, _ any) error { return nil }`, diff --git a/testdata/builtins.go b/testdata/builtins.go index b62be95..dfecaae 100644 --- a/testdata/builtins.go +++ b/testdata/builtins.go @@ -7,6 +7,7 @@ import ( // these packages are generated before the tests are run. "example.com/custom" "github.com/BurntSushi/toml" + "github.com/jmoiron/sqlx" "github.com/mitchellh/mapstructure" "gopkg.in/yaml.v3" ) @@ -43,11 +44,37 @@ type User struct { /* want "`User` should be annotated with the `mapstructure` tag as it is passed to `mapstructure.WeakDecode` at testdata(/|\\\\)src(/|\\\\)builtins(/|\\\\)builtins.go" "`User` should be annotated with the `mapstructure` tag as it is passed to `mapstructure.WeakDecodeMetadata` at testdata(/|\\\\)src(/|\\\\)builtins(/|\\\\)builtins.go" + "`User` should be annotated with the `db` tag as it is passed to `sqlx.Get` at testdata(/|\\\\)src(/|\\\\)builtins(/|\\\\)builtins.go" + "`User` should be annotated with the `db` tag as it is passed to `sqlx.GetContext` at testdata(/|\\\\)src(/|\\\\)builtins(/|\\\\)builtins.go" + "`User` should be annotated with the `db` tag as it is passed to `sqlx.Select` at testdata(/|\\\\)src(/|\\\\)builtins(/|\\\\)builtins.go" + "`User` should be annotated with the `db` tag as it is passed to `sqlx.SelectContext` at testdata(/|\\\\)src(/|\\\\)builtins(/|\\\\)builtins.go" + "`User` should be annotated with the `db` tag as it is passed to `sqlx.StructScan` at testdata(/|\\\\)src(/|\\\\)builtins(/|\\\\)builtins.go" + "`User` should be annotated with the `db` tag as it is passed to `sqlx.Conn.GetContext` at testdata(/|\\\\)src(/|\\\\)builtins(/|\\\\)builtins.go" + "`User` should be annotated with the `db` tag as it is passed to `sqlx.Conn.SelectContext` at testdata(/|\\\\)src(/|\\\\)builtins(/|\\\\)builtins.go" + "`User` should be annotated with the `db` tag as it is passed to `sqlx.DB.Get` at testdata(/|\\\\)src(/|\\\\)builtins(/|\\\\)builtins.go" + "`User` should be annotated with the `db` tag as it is passed to `sqlx.DB.GetContext` at testdata(/|\\\\)src(/|\\\\)builtins(/|\\\\)builtins.go" + "`User` should be annotated with the `db` tag as it is passed to `sqlx.DB.Select` at testdata(/|\\\\)src(/|\\\\)builtins(/|\\\\)builtins.go" + "`User` should be annotated with the `db` tag as it is passed to `sqlx.DB.SelectContext` at testdata(/|\\\\)src(/|\\\\)builtins(/|\\\\)builtins.go" + "`User` should be annotated with the `db` tag as it is passed to `sqlx.NamedStmt.Get` at testdata(/|\\\\)src(/|\\\\)builtins(/|\\\\)builtins.go" + "`User` should be annotated with the `db` tag as it is passed to `sqlx.NamedStmt.GetContext` at testdata(/|\\\\)src(/|\\\\)builtins(/|\\\\)builtins.go" + "`User` should be annotated with the `db` tag as it is passed to `sqlx.NamedStmt.Select` at testdata(/|\\\\)src(/|\\\\)builtins(/|\\\\)builtins.go" + "`User` should be annotated with the `db` tag as it is passed to `sqlx.NamedStmt.SelectContext` at testdata(/|\\\\)src(/|\\\\)builtins(/|\\\\)builtins.go" + "`User` should be annotated with the `db` tag as it is passed to `sqlx.Row.StructScan` at testdata(/|\\\\)src(/|\\\\)builtins(/|\\\\)builtins.go" + "`User` should be annotated with the `db` tag as it is passed to `sqlx.Rows.StructScan` at testdata(/|\\\\)src(/|\\\\)builtins(/|\\\\)builtins.go" + "`User` should be annotated with the `db` tag as it is passed to `sqlx.Stmt.Get` at testdata(/|\\\\)src(/|\\\\)builtins(/|\\\\)builtins.go" + "`User` should be annotated with the `db` tag as it is passed to `sqlx.Stmt.GetContext` at testdata(/|\\\\)src(/|\\\\)builtins(/|\\\\)builtins.go" + "`User` should be annotated with the `db` tag as it is passed to `sqlx.Stmt.Select` at testdata(/|\\\\)src(/|\\\\)builtins(/|\\\\)builtins.go" + "`User` should be annotated with the `db` tag as it is passed to `sqlx.Stmt.SelectContext` at testdata(/|\\\\)src(/|\\\\)builtins(/|\\\\)builtins.go" + "`User` should be annotated with the `db` tag as it is passed to `sqlx.Tx.Get` at testdata(/|\\\\)src(/|\\\\)builtins(/|\\\\)builtins.go" + "`User` should be annotated with the `db` tag as it is passed to `sqlx.Tx.GetContext` at testdata(/|\\\\)src(/|\\\\)builtins(/|\\\\)builtins.go" + "`User` should be annotated with the `db` tag as it is passed to `sqlx.Tx.Select` at testdata(/|\\\\)src(/|\\\\)builtins(/|\\\\)builtins.go" + "`User` should be annotated with the `db` tag as it is passed to `sqlx.Tx.SelectContext` at testdata(/|\\\\)src(/|\\\\)builtins(/|\\\\)builtins.go" + "`User` should be annotated with the `custom` tag as it is passed to `custom.Marshal` at testdata(/|\\\\)src(/|\\\\)builtins(/|\\\\)builtins.go" "`User` should be annotated with the `custom` tag as it is passed to `custom.Unmarshal` at testdata(/|\\\\)src(/|\\\\)builtins(/|\\\\)builtins.go" */ Name string - Email string `json:"email" xml:"email" yaml:"email" toml:"email" mapstructure:"email" custom:"email"` + Email string `json:"email" xml:"email" yaml:"email" toml:"email" mapstructure:"email" db:"email" custom:"email"` } func testJSON() { @@ -96,6 +123,35 @@ func testMapstructure() { mapstructure.WeakDecodeMetadata(nil, &user, nil) } +func testSQLX() { + var user User + sqlx.Get(nil, &user, "") + sqlx.GetContext(nil, nil, &user, "") + sqlx.Select(nil, &user, "") + sqlx.SelectContext(nil, nil, &user, "") + sqlx.StructScan(nil, &user) + new(sqlx.Conn).GetContext(nil, &user, "") + new(sqlx.Conn).SelectContext(nil, &user, "") + new(sqlx.DB).Get(&user, "") + new(sqlx.DB).GetContext(nil, &user, "") + new(sqlx.DB).Select(&user, "") + new(sqlx.DB).SelectContext(nil, &user, "") + new(sqlx.NamedStmt).Get(&user, nil) + new(sqlx.NamedStmt).GetContext(nil, &user, nil) + new(sqlx.NamedStmt).Select(&user, nil) + new(sqlx.NamedStmt).SelectContext(nil, &user, nil) + new(sqlx.Row).StructScan(&user) + new(sqlx.Rows).StructScan(&user) + new(sqlx.Stmt).Get(&user) + new(sqlx.Stmt).GetContext(nil, &user) + new(sqlx.Stmt).Select(&user) + new(sqlx.Stmt).SelectContext(nil, &user) + new(sqlx.Tx).Get(&user, "") + new(sqlx.Tx).GetContext(nil, &user, "") + new(sqlx.Tx).Select(&user, "") + new(sqlx.Tx).SelectContext(nil, &user, "") +} + func testCustom() { var user User custom.Marshal(user) From 07b694dfe53fb7e3715180fa8daac3bcd066e149 Mon Sep 17 00:00:00 2001 From: Tom <73077675+tmzane@users.noreply.github.com> Date: Sat, 27 May 2023 01:07:44 +0300 Subject: [PATCH 2/2] docs: update the example for custom functions --- README.md | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 21b9e89..621c7a9 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,7 @@ The following packages are supported out of the box: * [`gopkg.in/yaml.v3`][4] * [`github.com/BurntSushi/toml`][5] * [`github.com/mitchellh/mapstructure`][6] +* [`github.com/jmoiron/sqlx`][7] In addition, any [custom package](#custom-packages) can be added to the list. @@ -62,7 +63,7 @@ If you'd rather prefer to use `musttag` standalone, you can install it via `brew brew install tmzane/tap/musttag ``` -...or download a prebuilt binary from the [Releases][8] page. +...or download a prebuilt binary from the [Releases][9] page. Then run it either directly or as a `go vet` tool: @@ -74,24 +75,24 @@ go vet -vettool=$(which musttag) ./... To enable reporting a custom function, you need to add its description to `.golangci.yml`. -The following is an example of adding support for the `sqlx.Get` function from [`github.com/jmoiron/sqlx`][7]: +The following is an example of adding support for the `hclsimple.DecodeFile` function from [`github.com/hashicorp/hcl`][8]: ```yaml linters-settings: musttag: functions: # The full name of the function, including the package. - - name: github.com/jmoiron/sqlx.Get + - name: github.com/hashicorp/hcl/v2/hclsimple.DecodeFile # The struct tag whose presence should be ensured. - tag: db + tag: hcl # The position of the argument to check. - arg-pos: 1 + arg-pos: 2 ``` The same can be done via the `-fn=name:tag:arg-pos` flag when using `musttag` standalone: ```shell -musttag -fn="github.com/jmoiron/sqlx.Get:db:1" ./... +musttag -fn="github.com/hashicorp/hcl/v2/hclsimple.DecodeFile:hcl:2" ./... ``` [1]: https://github.com/uber-go/guide/blob/master/style.md#use-field-tags-in-marshaled-structs @@ -101,4 +102,5 @@ musttag -fn="github.com/jmoiron/sqlx.Get:db:1" ./... [5]: https://github.com/BurntSushi/toml [6]: https://github.com/mitchellh/mapstructure [7]: https://github.com/jmoiron/sqlx -[8]: https://github.com/tmzane/musttag/releases +[8]: https://github.com/hashicorp/hcl +[9]: https://github.com/tmzane/musttag/releases