Add MutablColumns list into autogen sql builder table.

This commit is contained in:
go-jet 2019-06-30 11:53:35 +02:00
parent 67e6fca0ce
commit d6c59deb1f
11 changed files with 219 additions and 59 deletions

View file

@ -36,7 +36,7 @@ func (e *expressionTableImpl) Alias() string {
return e.alias return e.alias
} }
func (e *expressionTableImpl) columns() []Column { func (e *expressionTableImpl) columns() []column {
return nil return nil
} }

View file

@ -16,10 +16,24 @@ func (t TableInfo) Name() string {
return t.name return t.name
} }
func (t TableInfo) IsUnique(columnName string) bool { func (t TableInfo) IsPrimaryKey(columnName string) bool {
return t.PrimaryKeys[columnName] return t.PrimaryKeys[columnName]
} }
func (t TableInfo) MutableColumns() []ColumnInfo {
ret := []ColumnInfo{}
for _, column := range t.Columns {
if t.IsPrimaryKey(column.Name) {
continue
}
ret = append(ret, column)
}
return ret
}
func (t TableInfo) GetImports() []string { func (t TableInfo) GetImports() []string {
imports := map[string]string{} imports := map[string]string{}

View file

@ -36,7 +36,8 @@ type {{.GoStructName}} struct {
{{camelize .Name}} jet.Column{{.SqlBuilderColumnType}} {{camelize .Name}} jet.Column{{.SqlBuilderColumnType}}
{{- end}} {{- end}}
AllColumns jet.ColumnList AllColumns jet.ColumnList
MutableColumns jet.ColumnList
} }
// creates new {{.GoStructName}} with assigned alias // creates new {{.GoStructName}} with assigned alias
@ -63,7 +64,8 @@ func new{{.GoStructName}}() *{{.GoStructName}} {
{{camelize .Name}}: {{camelize .Name}}Column, {{camelize .Name}}: {{camelize .Name}}Column,
{{- end}} {{- end}}
AllColumns: jet.ColumnList{ {{template "column-list" .Columns}} }, AllColumns: jet.ColumnList{ {{template "column-list" .Columns}} },
MutableColumns: jet.ColumnList{ {{template "column-list" .MutableColumns}} },
} }
} }
@ -82,7 +84,7 @@ import (
type {{camelize .Name}} struct { type {{camelize .Name}} struct {
{{- range .Columns}} {{- range .Columns}}
{{camelize .Name}} {{.GoModelType}} ` + "{{.GoModelTag ($.IsUnique .Name)}}" + ` {{camelize .Name}} {{.GoModelType}} ` + "{{.GoModelTag ($.IsPrimaryKey .Name)}}" + `
{{- end}} {{- end}}
} }
` `

View file

@ -10,10 +10,13 @@ import (
type InsertStatement interface { type InsertStatement interface {
Statement Statement
// Add a row of values to the insert Statement. // Insert row of values
VALUES(value interface{}, values ...interface{}) InsertStatement VALUES(value interface{}, values ...interface{}) InsertStatement
// Model structure mapped to column names // Insert row of values, where value for each column is extracted from filed of structure data.
USING(data interface{}) InsertStatement // If data is not struct or there is no field for every column selected, this method will panic.
MODEL(data interface{}) InsertStatement
MODELS(data interface{}) InsertStatement
QUERY(selectStatement SelectStatement) InsertStatement QUERY(selectStatement SelectStatement) InsertStatement
@ -40,8 +43,13 @@ func (i *insertStatementImpl) VALUES(value interface{}, values ...interface{}) I
return i return i
} }
func (i *insertStatementImpl) USING(data interface{}) InsertStatement { func (i *insertStatementImpl) MODEL(data interface{}) InsertStatement {
i.rows = append(i.rows, unwindRowFromModel(i.columns, data)) i.rows = append(i.rows, unwindRowFromModel(i.getColumns(), data))
return i
}
func (i *insertStatementImpl) MODELS(data interface{}) InsertStatement {
i.rows = append(i.rows, unwindRowsFromModels(i.getColumns(), data)...)
return i return i
} }
@ -55,6 +63,14 @@ func (i *insertStatementImpl) QUERY(selectStatement SelectStatement) InsertState
return i return i
} }
func (i *insertStatementImpl) getColumns() []column {
if len(i.columns) > 0 {
return i.columns
}
return i.table.columns()
}
func (i *insertStatementImpl) DebugSql() (query string, err error) { func (i *insertStatementImpl) DebugSql() (query string, err error) {
return debugSql(i) return debugSql(i)
} }
@ -107,10 +123,6 @@ func (i *insertStatementImpl) Sql() (sql string, args []interface{}, err error)
queryData.newLine() queryData.newLine()
queryData.writeString("(") queryData.writeString("(")
if len(row) != len(i.columns) {
return "", nil, errors.New("number of values does not match number of columns")
}
err = serializeClauseList(insert_statement, row, queryData) err = serializeClauseList(insert_statement, row, queryData)
if err != nil { if err != nil {

View file

@ -8,7 +8,6 @@ import (
func TestInvalidInsert(t *testing.T) { func TestInvalidInsert(t *testing.T) {
assertStatementErr(t, table1.INSERT(table1Col1), "no row values or query specified") assertStatementErr(t, table1.INSERT(table1Col1), "no row values or query specified")
assertStatementErr(t, table1.INSERT(table1Col1, table1ColFloat).VALUES(11), "number of values does not match number of columns")
assertStatementErr(t, table1.INSERT(nil).VALUES(1), "nil column in columns list") assertStatementErr(t, table1.INSERT(nil).VALUES(1), "nil column in columns list")
} }
@ -79,8 +78,8 @@ func TestInsertValuesFromModel(t *testing.T) {
} }
stmt := table1.INSERT(table1Col1, table1ColFloat). stmt := table1.INSERT(table1Col1, table1ColFloat).
USING(toInsert). MODEL(toInsert).
USING(&toInsert) MODEL(&toInsert)
expectedSql := ` expectedSql := `
INSERT INTO db.table1 (col1, col_float) VALUES INSERT INTO db.table1 (col1, col_float) VALUES
@ -108,7 +107,7 @@ func TestInsertValuesFromModelColumnMismatch(t *testing.T) {
table1. table1.
INSERT(table1Col1, table1ColFloat). INSERT(table1Col1, table1ColFloat).
USING(newData) MODEL(newData)
} }
func TestInsertFromNonStructModel(t *testing.T) { func TestInsertFromNonStructModel(t *testing.T) {
@ -118,7 +117,7 @@ func TestInsertFromNonStructModel(t *testing.T) {
assert.Equal(t, r, "argument mismatch: expected struct, got []int") assert.Equal(t, r, "argument mismatch: expected struct, got []int")
}() }()
table2.INSERT(table2ColInt).USING([]int{}) table2.INSERT(table2ColInt).MODEL([]int{})
} }
func TestInsertQuery(t *testing.T) { func TestInsertQuery(t *testing.T) {

View file

@ -6,6 +6,10 @@ import (
"errors" "errors"
) )
type table interface {
columns() []column
}
type readableTable interface { type readableTable interface {
// Generates a select query on the current tableName. // Generates a select query on the current tableName.
SELECT(projection projection, projections ...projection) SelectStatement SELECT(projection projection, projections ...projection) SelectStatement
@ -24,13 +28,11 @@ type readableTable interface {
// Creates a cross join tableName Expression using onCondition. // Creates a cross join tableName Expression using onCondition.
CROSS_JOIN(table ReadableTable) ReadableTable CROSS_JOIN(table ReadableTable) ReadableTable
columns() []Column
} }
// The sql tableName write interface. // The sql tableName write interface.
type writableTable interface { type writableTable interface {
INSERT(column column, columns ...column) InsertStatement INSERT(columns ...column) InsertStatement
UPDATE(column column, columns ...column) UpdateStatement UPDATE(column column, columns ...column) UpdateStatement
DELETE() DeleteStatement DELETE() DeleteStatement
@ -38,16 +40,19 @@ type writableTable interface {
} }
type ReadableTable interface { type ReadableTable interface {
table
readableTable readableTable
clause clause
} }
type WritableTable interface { type WritableTable interface {
table
writableTable writableTable
clause clause
} }
type Table interface { type Table interface {
table
readableTable readableTable
writableTable writableTable
clause clause
@ -92,8 +97,14 @@ type writableTableInterfaceImpl struct {
parent WritableTable parent WritableTable
} }
func (w *writableTableInterfaceImpl) INSERT(column column, columns ...column) InsertStatement { func (w *writableTableInterfaceImpl) INSERT(columns ...column) InsertStatement {
return newInsertStatement(w.parent, unwindColumns(column, columns...)) //columnList := unwidColumnList(columns)
//
//if len(columns) == 0 {
// columnList = w.parent.columns()
//}
return newInsertStatement(w.parent, unwidColumnList(columns))
} }
func (w *writableTableInterfaceImpl) UPDATE(column column, columns ...column) UpdateStatement { func (w *writableTableInterfaceImpl) UPDATE(column column, columns ...column) UpdateStatement {
@ -153,8 +164,14 @@ func (t *tableImpl) TableName() string {
return t.name return t.name
} }
func (t *tableImpl) columns() []Column { func (t *tableImpl) columns() []column {
return t.columnList ret := []column{}
for _, col := range t.columnList {
ret = append(ret, col)
}
return ret
} }
func (t *tableImpl) serialize(statement statementType, out *queryData, options ...serializeOption) error { func (t *tableImpl) serialize(statement statementType, out *queryData, options ...serializeOption) error {
@ -220,7 +237,7 @@ func (t *joinTable) TableName() string {
return "" return ""
} }
func (t *joinTable) columns() []Column { func (t *joinTable) columns() []column {
return append(t.lhs.columns(), t.rhs.columns()...) return append(t.lhs.columns(), t.rhs.columns()...)
} }
@ -289,3 +306,19 @@ func unwindColumns(column1 column, columns ...column) []column {
return columnList return columnList
} }
func unwidColumnList(columns []column) []column {
ret := []column{}
for _, col := range columns {
if columnList, ok := col.(ColumnList); ok {
for _, c := range columnList {
ret = append(ret, c)
}
} else {
ret = append(ret, col)
}
}
return ret
}

View file

@ -26,8 +26,8 @@ func TestAllTypesSelect(t *testing.T) {
func TestAllTypesInsertModel(t *testing.T) { func TestAllTypesInsertModel(t *testing.T) {
query := AllTypes.INSERT(AllTypes.AllColumns). query := AllTypes.INSERT(AllTypes.AllColumns).
USING(allTypesRow0). MODEL(allTypesRow0).
USING(&allTypesRow1). MODEL(&allTypesRow1).
RETURNING(AllTypes.AllColumns) RETURNING(AllTypes.AllColumns)
dest := []model.AllTypes{} dest := []model.AllTypes{}

View file

@ -144,11 +144,10 @@ VALUES (1, 1, 300, 300, 50000, 5000, 11.44, 11.44, 55.77, 55.77, 99.1, 99.1, 111
DROP TABLE IF EXISTS test_sample.link; DROP TABLE IF EXISTS test_sample.link;
CREATE TABLE IF NOT EXISTS test_sample.link ( CREATE TABLE IF NOT EXISTS test_sample.link (
ID serial PRIMARY KEY, id serial PRIMARY KEY,
url VARCHAR (255) NOT NULL, url VARCHAR (255) NOT NULL,
name VARCHAR (255) NOT NULL, name VARCHAR (255) NOT NULL,
description VARCHAR (255), description VARCHAR (255)
rel VARCHAR (50)
); );
INSERT INTO test_sample.link (ID, url, name, description) VALUES INSERT INTO test_sample.link (ID, url, name, description) VALUES

View file

@ -9,22 +9,20 @@ import (
) )
func TestInsertValues(t *testing.T) { func TestInsertValues(t *testing.T) {
cleanUpLinkTable(t) cleanUpLinkTable(t)
var expectedSql = ` var expectedSql = `
INSERT INTO test_sample.link (id, url, name, rel) VALUES INSERT INTO test_sample.link (id, url, name, description) VALUES
(100, 'http://www.postgresqltutorial.com', 'PostgreSQL Tutorial', DEFAULT), (100, 'http://www.postgresqltutorial.com', 'PostgreSQL Tutorial', DEFAULT),
(101, 'http://www.google.com', 'Google', DEFAULT), (101, 'http://www.google.com', 'Google', DEFAULT),
(102, 'http://www.yahoo.com', 'Yahoo', NULL) (102, 'http://www.yahoo.com', 'Yahoo', NULL)
RETURNING link.id AS "link.id", RETURNING link.id AS "link.id",
link.url AS "link.url", link.url AS "link.url",
link.name AS "link.name", link.name AS "link.name",
link.description AS "link.description", link.description AS "link.description";
link.rel AS "link.rel";
` `
insertQuery := Link.INSERT(Link.ID, Link.URL, Link.Name, Link.Rel). insertQuery := Link.INSERT(Link.ID, Link.URL, Link.Name, Link.Description).
VALUES(100, "http://www.postgresqltutorial.com", "PostgreSQL Tutorial", DEFAULT). VALUES(100, "http://www.postgresqltutorial.com", "PostgreSQL Tutorial", DEFAULT).
VALUES(101, "http://www.google.com", "Google", DEFAULT). VALUES(101, "http://www.google.com", "Google", DEFAULT).
VALUES(102, "http://www.yahoo.com", "Yahoo", nil). VALUES(102, "http://www.yahoo.com", "Yahoo", nil).
@ -47,21 +45,18 @@ RETURNING link.id AS "link.id",
ID: 100, ID: 100,
URL: "http://www.postgresqltutorial.com", URL: "http://www.postgresqltutorial.com",
Name: "PostgreSQL Tutorial", Name: "PostgreSQL Tutorial",
Rel: nil,
}) })
assert.DeepEqual(t, insertedLinks[1], model.Link{ assert.DeepEqual(t, insertedLinks[1], model.Link{
ID: 101, ID: 101,
URL: "http://www.google.com", URL: "http://www.google.com",
Name: "Google", Name: "Google",
Rel: nil,
}) })
assert.DeepEqual(t, insertedLinks[2], model.Link{ assert.DeepEqual(t, insertedLinks[2], model.Link{
ID: 102, ID: 102,
URL: "http://www.yahoo.com", URL: "http://www.yahoo.com",
Name: "Yahoo", Name: "Yahoo",
Rel: nil,
}) })
allLinks := []model.Link{} allLinks := []model.Link{}
@ -76,7 +71,24 @@ RETURNING link.id AS "link.id",
assert.DeepEqual(t, insertedLinks, allLinks) assert.DeepEqual(t, insertedLinks, allLinks)
} }
func TestInsertDataObject(t *testing.T) { func TestInsertEmptyColumnList(t *testing.T) {
cleanUpLinkTable(t)
expectedSql := `
INSERT INTO test_sample.link VALUES
(100, 'http://www.postgresqltutorial.com', 'PostgreSQL Tutorial', DEFAULT);
`
stmt := Link.INSERT().
VALUES(100, "http://www.postgresqltutorial.com", "PostgreSQL Tutorial", DEFAULT)
assertStatementSql(t, stmt, expectedSql,
100, "http://www.postgresqltutorial.com", "PostgreSQL Tutorial")
assertExec(t, stmt, 1)
}
func TestInsertModelObject(t *testing.T) {
var expectedSql = ` var expectedSql = `
INSERT INTO test_sample.link (url, name) VALUES INSERT INTO test_sample.link (url, name) VALUES
('http://www.duckduckgo.com', 'Duck Duck go'); ('http://www.duckduckgo.com', 'Duck Duck go');
@ -85,12 +97,11 @@ INSERT INTO test_sample.link (url, name) VALUES
linkData := model.Link{ linkData := model.Link{
URL: "http://www.duckduckgo.com", URL: "http://www.duckduckgo.com",
Name: "Duck Duck go", Name: "Duck Duck go",
Rel: nil,
} }
query := Link. query := Link.
INSERT(Link.URL, Link.Name). INSERT(Link.URL, Link.Name).
USING(linkData) MODEL(linkData)
assertStatementSql(t, query, expectedSql, "http://www.duckduckgo.com", "Duck Duck go") assertStatementSql(t, query, expectedSql, "http://www.duckduckgo.com", "Duck Duck go")
@ -103,6 +114,75 @@ INSERT INTO test_sample.link (url, name) VALUES
assert.Equal(t, rowsAffected, int64(1)) assert.Equal(t, rowsAffected, int64(1))
} }
func TestInsertModelsObject(t *testing.T) {
expectedSql := `
INSERT INTO test_sample.link (url, name) VALUES
('http://www.postgresqltutorial.com', 'PostgreSQL Tutorial'),
('http://www.google.com', 'Google'),
('http://www.yahoo.com', 'Yahoo');
`
tutorial := model.Link{
URL: "http://www.postgresqltutorial.com",
Name: "PostgreSQL Tutorial",
}
google := model.Link{
URL: "http://www.google.com",
Name: "Google",
}
yahoo := model.Link{
URL: "http://www.yahoo.com",
Name: "Yahoo",
}
stmt := Link.
INSERT(Link.URL, Link.Name).
MODELS([]model.Link{tutorial, google, yahoo})
assertStatementSql(t, stmt, expectedSql,
"http://www.postgresqltutorial.com", "PostgreSQL Tutorial",
"http://www.google.com", "Google",
"http://www.yahoo.com", "Yahoo")
assertExec(t, stmt, 3)
}
func TestInsertUsingMutableColumns(t *testing.T) {
var expectedSql = `
INSERT INTO test_sample.link (url, name, description) VALUES
('http://www.postgresqltutorial.com', 'PostgreSQL Tutorial', DEFAULT),
('http://www.google.com', 'Google', NULL),
('http://www.google.com', 'Google', NULL),
('http://www.yahoo.com', 'Yahoo', NULL);
`
google := model.Link{
URL: "http://www.google.com",
Name: "Google",
}
yahoo := model.Link{
URL: "http://www.yahoo.com",
Name: "Yahoo",
}
stmt := Link.
INSERT(Link.MutableColumns).
VALUES("http://www.postgresqltutorial.com", "PostgreSQL Tutorial", DEFAULT).
MODEL(google).
MODELS([]model.Link{google, yahoo})
assertStatementSql(t, stmt, expectedSql,
"http://www.postgresqltutorial.com", "PostgreSQL Tutorial",
"http://www.google.com", "Google", nil,
"http://www.google.com", "Google", nil,
"http://www.yahoo.com", "Yahoo", nil)
assertExec(t, stmt, 4)
}
func TestInsertQuery(t *testing.T) { func TestInsertQuery(t *testing.T) {
_, err := Link.DELETE(). _, err := Link.DELETE().
WHERE(Link.ID.NOT_EQ(Int(0)).AND(Link.Name.EQ(String("Youtube")))). WHERE(Link.ID.NOT_EQ(Int(0)).AND(Link.Name.EQ(String("Youtube")))).
@ -119,8 +199,7 @@ INSERT INTO test_sample.link (url, name) (
RETURNING link.id AS "link.id", RETURNING link.id AS "link.id",
link.url AS "link.url", link.url AS "link.url",
link.name AS "link.name", link.name AS "link.name",
link.description AS "link.description", link.description AS "link.description";
link.rel AS "link.rel";
` `
query := Link. query := Link.

View file

@ -86,8 +86,7 @@ WHERE link.name = 'Ask'
RETURNING link.id AS "link.id", RETURNING link.id AS "link.id",
link.url AS "link.url", link.url AS "link.url",
link.name AS "link.name", link.name AS "link.name",
link.description AS "link.description", link.description AS "link.description";
link.rel AS "link.rel";
` `
stmt := Link. stmt := Link.
@ -120,12 +119,11 @@ func TestUpdateWithSelect(t *testing.T) {
expectedSql := ` expectedSql := `
UPDATE test_sample.link UPDATE test_sample.link
SET (id, url, name, description, rel) = ( SET (id, url, name, description) = (
SELECT link.id AS "link.id", SELECT link.id AS "link.id",
link.url AS "link.url", link.url AS "link.url",
link.name AS "link.name", link.name AS "link.name",
link.description AS "link.description", link.description AS "link.description"
link.rel AS "link.rel"
FROM test_sample.link FROM test_sample.link
WHERE link.id = 0 WHERE link.id = 0
) )
@ -148,7 +146,7 @@ func TestUpdateWithInvalidSelect(t *testing.T) {
var expectedSql = ` var expectedSql = `
UPDATE test_sample.link UPDATE test_sample.link
SET (id, url, name, description, rel) = ( SET (id, url, name, description) = (
SELECT link.id AS "link.id", SELECT link.id AS "link.id",
link.name AS "link.name" link.name AS "link.name"
FROM test_sample.link FROM test_sample.link
@ -177,10 +175,10 @@ func TestUpdateWithModelData(t *testing.T) {
expectedSql := ` expectedSql := `
UPDATE test_sample.link UPDATE test_sample.link
SET (id, url, name, description, rel) = (201, 'http://www.duckduckgo.com', 'DuckDuckGo', NULL, NULL) SET (id, url, name, description) = (201, 'http://www.duckduckgo.com', 'DuckDuckGo', NULL)
WHERE link.id = 201; WHERE link.id = 201;
` `
assertStatementSql(t, stmt, expectedSql, int32(201), "http://www.duckduckgo.com", "DuckDuckGo", nil, nil, int64(201)) assertStatementSql(t, stmt, expectedSql, int32(201), "http://www.duckduckgo.com", "DuckDuckGo", nil, int64(201))
assertExec(t, stmt, 1) assertExec(t, stmt, 1)
} }
@ -195,7 +193,7 @@ func TestUpdateWithModelDataAndPredefinedColumnList(t *testing.T) {
Name: "DuckDuckGo", Name: "DuckDuckGo",
} }
updateColumnList := ColumnList{Link.Rel, Link.Name, Link.URL} updateColumnList := ColumnList{Link.Description, Link.Name, Link.URL}
stmt := Link. stmt := Link.
UPDATE(updateColumnList). UPDATE(updateColumnList).
@ -204,7 +202,7 @@ func TestUpdateWithModelDataAndPredefinedColumnList(t *testing.T) {
var expectedSql = ` var expectedSql = `
UPDATE test_sample.link UPDATE test_sample.link
SET (rel, name, url) = (NULL, 'DuckDuckGo', 'http://www.duckduckgo.com') SET (description, name, url) = (NULL, 'DuckDuckGo', 'http://www.duckduckgo.com')
WHERE link.id = 201; WHERE link.id = 201;
` `
assertStatementSql(t, stmt, expectedSql, nil, "DuckDuckGo", "http://www.duckduckgo.com", int64(201)) assertStatementSql(t, stmt, expectedSql, nil, "DuckDuckGo", "http://www.duckduckgo.com", int64(201))
@ -252,7 +250,7 @@ func setupLinkTableForUpdateTest(t *testing.T) {
cleanUpLinkTable(t) cleanUpLinkTable(t)
_, err := Link.INSERT(Link.ID, Link.URL, Link.Name, Link.Rel). _, err := Link.INSERT(Link.ID, Link.URL, Link.Name, Link.Description).
VALUES(200, "http://www.postgresqltutorial.com", "PostgreSQL Tutorial", DEFAULT). VALUES(200, "http://www.postgresqltutorial.com", "PostgreSQL Tutorial", DEFAULT).
VALUES(201, "http://www.ask.com", "Ask", DEFAULT). VALUES(201, "http://www.ask.com", "Ask", DEFAULT).
VALUES(202, "http://www.ask.com", "Ask", DEFAULT). VALUES(202, "http://www.ask.com", "Ask", DEFAULT).

View file

@ -4,6 +4,7 @@ import (
"errors" "errors"
"github.com/serenize/snaker" "github.com/serenize/snaker"
"reflect" "reflect"
"strings"
) )
func serializeOrderByClauseList(statement statementType, orderByClauses []OrderByClause, out *queryData) error { func serializeOrderByClauseList(statement statementType, orderByClauses []OrderByClause, out *queryData) error {
@ -166,6 +167,21 @@ func unwindRowFromModel(columns []column, data interface{}) []clause {
return row return row
} }
func unwindRowsFromModels(columns []column, data interface{}) [][]clause {
sliceValue := reflect.Indirect(reflect.ValueOf(data))
mustBe(sliceValue, reflect.Slice)
rows := [][]clause{}
for i := 0; i < sliceValue.Len(); i++ {
structValue := sliceValue.Index(i)
rows = append(rows, unwindRowFromModel(columns, structValue.Interface()))
}
return rows
}
func unwindRowFromValues(value interface{}, values []interface{}) []clause { func unwindRowFromValues(value interface{}, values []interface{}) []clause {
row := []clause{} row := []clause{}
@ -178,8 +194,16 @@ func unwindRowFromValues(value interface{}, values []interface{}) []clause {
return row return row
} }
func mustBe(v reflect.Value, expected reflect.Kind) { func mustBe(v reflect.Value, expectedKinds ...reflect.Kind) {
if k := v.Kind(); k != expected { indirectV := reflect.Indirect(v)
panic("argument mismatch: expected " + expected.String() + ", got " + v.Type().String()) types := []string{}
for _, expectedKind := range expectedKinds {
types = append(types, expectedKind.String())
if k := indirectV.Kind(); k == expectedKind {
return
}
} }
panic("argument mismatch: expected " + strings.Join(types, " or ") + ", got " + v.Type().String())
} }