Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support sqlx #50

Merged
merged 2 commits into from
May 26, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
16 changes: 9 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand All @@ -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:

Expand All @@ -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
Expand All @@ -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
27 changes: 27 additions & 0 deletions builtins.go
Original file line number Diff line number Diff line change
Expand Up @@ -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},
}
38 changes: 38 additions & 0 deletions musttag_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 }`,
Expand Down
58 changes: 57 additions & 1 deletion testdata/builtins.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)
Expand Down Expand Up @@ -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() {
Expand Down Expand Up @@ -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)
Expand Down