Compare commits
No commits in common. "04676461ff333c2fce84301b910653dc17b09035" and "5973a2a4b732de414c376b42557650abe9145755" have entirely different histories.
04676461ff
...
5973a2a4b7
1
.gitignore
vendored
1
.gitignore
vendored
@ -3,4 +3,3 @@ sample_data/data
|
|||||||
# Legacy versions
|
# Legacy versions
|
||||||
.cmd-old/
|
.cmd-old/
|
||||||
pkg/.testapp
|
pkg/.testapp
|
||||||
.claude
|
|
||||||
|
|||||||
@ -26,14 +26,6 @@ TODO: language-server-for-TODO.txt
|
|||||||
TODO: auto-migration-checker
|
TODO: auto-migration-checker
|
||||||
- Use `pkg/schema` to test whether a base schema plus a migration equals a new schema
|
- Use `pkg/schema` to test whether a base schema plus a migration equals a new schema
|
||||||
|
|
||||||
TODO: codegen SaveXyz update path doesn't check foreign keys
|
|
||||||
- Insert path has FK error handling, but the update path wraps everything in Must
|
|
||||||
- An update that violates an FK constraint will panic instead of returning an error
|
|
||||||
|
|
||||||
TODO: codegen `without rowid` tables properly
|
TODO: codegen `without rowid` tables properly
|
||||||
|
|
||||||
TODO: generated test file inclues global test DB setup, which is wrong
|
TODO: generated test file inclues global test DB setup, which is wrong
|
||||||
|
|
||||||
TODO: join-tables
|
|
||||||
- handle codegen for without rowid tables properly
|
|
||||||
- add "get all "
|
|
||||||
|
|||||||
@ -7,7 +7,6 @@ sudo docker run --rm -it \
|
|||||||
-v "$(go env GOMODCACHE):/gocache-vol/mod-cache" \
|
-v "$(go env GOMODCACHE):/gocache-vol/mod-cache" \
|
||||||
-e GOMODCACHE=/gocache-vol/mod-cache \
|
-e GOMODCACHE=/gocache-vol/mod-cache \
|
||||||
-e GOLANGCI_LINT_CACHE=/gocache-vol/lint-cache \
|
-e GOLANGCI_LINT_CACHE=/gocache-vol/lint-cache \
|
||||||
-v /memory:/memory \
|
|
||||||
--workdir /code \
|
--workdir /code \
|
||||||
--net host \
|
--net host \
|
||||||
gas
|
gas
|
||||||
|
|||||||
@ -1,11 +0,0 @@
|
|||||||
package modelgenerate
|
|
||||||
|
|
||||||
import "go/ast"
|
|
||||||
|
|
||||||
// mustCall wraps a call expression in Must(...), producing AST for Must(inner).
|
|
||||||
func mustCall(inner ast.Expr) *ast.CallExpr {
|
|
||||||
return &ast.CallExpr{
|
|
||||||
Fun: ast.NewIdent("Must"),
|
|
||||||
Args: []ast.Expr{inner},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -13,18 +13,12 @@ import (
|
|||||||
"git.offline-twitter.com/offline-labs/gas-stack/pkg/textutils"
|
"git.offline-twitter.com/offline-labs/gas-stack/pkg/textutils"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ---------------
|
func GenerateIDType(table schema.Table) *ast.GenDecl {
|
||||||
// Helpers
|
// e.g., `type FoodID int`
|
||||||
// ---------------
|
return &ast.GenDecl{
|
||||||
|
Tok: token.TYPE,
|
||||||
var (
|
Specs: []ast.Spec{&ast.TypeSpec{Name: ast.NewIdent(table.TypeIDName), Type: ast.NewIdent("int")}},
|
||||||
dbRecv = &ast.FieldList{List: []*ast.Field{{Names: []*ast.Ident{ast.NewIdent("db")}, Type: ast.NewIdent("DB")}}}
|
}
|
||||||
dbDB = &ast.SelectorExpr{X: ast.NewIdent("db"), Sel: ast.NewIdent("DB")}
|
|
||||||
fmtErrorf = &ast.SelectorExpr{X: ast.NewIdent("fmt"), Sel: ast.NewIdent("Errorf")}
|
|
||||||
)
|
|
||||||
|
|
||||||
func SQLFieldsConstIdent(tbl schema.Table) *ast.Ident {
|
|
||||||
return ast.NewIdent(strings.ToLower(tbl.GoTypeName) + "SQLFields")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func fkFieldName(col schema.Column) string {
|
func fkFieldName(col schema.Column) string {
|
||||||
@ -35,19 +29,6 @@ func fkFieldName(col schema.Column) string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------
|
|
||||||
// Generators
|
|
||||||
// ---------------
|
|
||||||
|
|
||||||
// GenerateIDType produces an AST for the model's ID field.
|
|
||||||
func GenerateIDType(table schema.Table) *ast.GenDecl {
|
|
||||||
// e.g., `type FoodID int`
|
|
||||||
return &ast.GenDecl{
|
|
||||||
Tok: token.TYPE,
|
|
||||||
Specs: []ast.Spec{&ast.TypeSpec{Name: ast.NewIdent(table.TypeIDName), Type: ast.NewIdent("int")}},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// GenerateModelAST produces an AST for a struct type corresponding to the model.
|
// GenerateModelAST produces an AST for a struct type corresponding to the model.
|
||||||
// TODO: generate the right field types here based on column types.
|
// TODO: generate the right field types here based on column types.
|
||||||
func GenerateModelAST(table schema.Table) *ast.GenDecl {
|
func GenerateModelAST(table schema.Table) *ast.GenDecl {
|
||||||
@ -222,7 +203,7 @@ func GenerateSaveItemFunc(tbl schema.Table) *ast.FuncDecl {
|
|||||||
Tok: token.ASSIGN,
|
Tok: token.ASSIGN,
|
||||||
Rhs: []ast.Expr{
|
Rhs: []ast.Expr{
|
||||||
&ast.CallExpr{
|
&ast.CallExpr{
|
||||||
Fun: &ast.SelectorExpr{X: dbDB, Sel: ast.NewIdent("Get")},
|
Fun: &ast.SelectorExpr{X: &ast.SelectorExpr{X: ast.NewIdent("db"), Sel: ast.NewIdent("DB")}, Sel: ast.NewIdent("Get")},
|
||||||
Args: []ast.Expr{
|
Args: []ast.Expr{
|
||||||
&ast.CallExpr{Fun: ast.NewIdent("new"), Args: []ast.Expr{ast.NewIdent("int")}},
|
&ast.CallExpr{Fun: ast.NewIdent("new"), Args: []ast.Expr{ast.NewIdent("int")}},
|
||||||
&ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf("`select 1 from %s where rowid = ?`", col.ForeignKeyTargetTable)},
|
&ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf("`select 1 from %s where rowid = ?`", col.ForeignKeyTargetTable)},
|
||||||
@ -288,17 +269,17 @@ func GenerateSaveItemFunc(tbl schema.Table) *ast.FuncDecl {
|
|||||||
// Do create
|
// Do create
|
||||||
List: append(
|
List: append(
|
||||||
func() []ast.Stmt {
|
func() []ast.Stmt {
|
||||||
ret1 := []ast.Stmt{}
|
ret := []ast.Stmt{}
|
||||||
if hasCreatedAt {
|
if hasCreatedAt {
|
||||||
// Auto-timestamps: created_at
|
// Auto-timestamps: created_at
|
||||||
ret1 = append(ret1, &ast.AssignStmt{
|
ret = append(ret, &ast.AssignStmt{
|
||||||
Lhs: []ast.Expr{&ast.SelectorExpr{X: ast.NewIdent(tbl.VarName), Sel: ast.NewIdent("CreatedAt")}},
|
Lhs: []ast.Expr{&ast.SelectorExpr{X: ast.NewIdent(tbl.VarName), Sel: ast.NewIdent("CreatedAt")}},
|
||||||
Tok: token.ASSIGN,
|
Tok: token.ASSIGN,
|
||||||
Rhs: []ast.Expr{&ast.CallExpr{Fun: ast.NewIdent("TimestampNow"), Args: []ast.Expr{}}},
|
Rhs: []ast.Expr{&ast.CallExpr{Fun: ast.NewIdent("TimestampNow"), Args: []ast.Expr{}}},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
namedExecStmt := &ast.CallExpr{
|
namedExecStmt := &ast.CallExpr{
|
||||||
Fun: &ast.SelectorExpr{X: dbDB, Sel: ast.NewIdent("NamedExec")},
|
Fun: &ast.SelectorExpr{X: ast.NewIdent("db.DB"), Sel: ast.NewIdent("NamedExec")},
|
||||||
Args: []ast.Expr{
|
Args: []ast.Expr{
|
||||||
&ast.BasicLit{Kind: token.STRING, Value: "`" + insertStmt + "`"},
|
&ast.BasicLit{Kind: token.STRING, Value: "`" + insertStmt + "`"},
|
||||||
ast.NewIdent(tbl.VarName),
|
ast.NewIdent(tbl.VarName),
|
||||||
@ -306,14 +287,17 @@ func GenerateSaveItemFunc(tbl schema.Table) *ast.FuncDecl {
|
|||||||
}
|
}
|
||||||
if !hasFks {
|
if !hasFks {
|
||||||
// No foreign key checking needed; just use `Must` for brevity
|
// No foreign key checking needed; just use `Must` for brevity
|
||||||
return append(ret1, &ast.AssignStmt{
|
return append(ret, &ast.AssignStmt{
|
||||||
Lhs: []ast.Expr{ast.NewIdent("result")},
|
Lhs: []ast.Expr{ast.NewIdent("result")},
|
||||||
Tok: token.DEFINE,
|
Tok: token.DEFINE,
|
||||||
Rhs: []ast.Expr{mustCall(namedExecStmt)},
|
Rhs: []ast.Expr{&ast.CallExpr{
|
||||||
|
Fun: ast.NewIdent("Must"),
|
||||||
|
Args: []ast.Expr{namedExecStmt},
|
||||||
|
}},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return append(ret1,
|
return append(ret,
|
||||||
// result, err := db.DB.NamedExec(`...`, u)
|
// result, err := db.DB.NamedExec(`...`, u)
|
||||||
&ast.AssignStmt{
|
&ast.AssignStmt{
|
||||||
Lhs: []ast.Expr{
|
Lhs: []ast.Expr{
|
||||||
@ -373,10 +357,13 @@ func GenerateSaveItemFunc(tbl schema.Table) *ast.FuncDecl {
|
|||||||
Tok: token.ASSIGN,
|
Tok: token.ASSIGN,
|
||||||
Rhs: []ast.Expr{&ast.CallExpr{
|
Rhs: []ast.Expr{&ast.CallExpr{
|
||||||
Fun: ast.NewIdent(tbl.TypeIDName),
|
Fun: ast.NewIdent(tbl.TypeIDName),
|
||||||
Args: []ast.Expr{mustCall(&ast.CallExpr{
|
Args: []ast.Expr{&ast.CallExpr{
|
||||||
Fun: &ast.SelectorExpr{X: ast.NewIdent("result"), Sel: ast.NewIdent("LastInsertId")},
|
Fun: ast.NewIdent("Must"),
|
||||||
Args: []ast.Expr{},
|
Args: []ast.Expr{&ast.CallExpr{
|
||||||
})},
|
Fun: &ast.SelectorExpr{X: ast.NewIdent("result"), Sel: ast.NewIdent("LastInsertId")},
|
||||||
|
Args: []ast.Expr{},
|
||||||
|
}},
|
||||||
|
}},
|
||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@ -387,22 +374,28 @@ func GenerateSaveItemFunc(tbl schema.Table) *ast.FuncDecl {
|
|||||||
&ast.AssignStmt{
|
&ast.AssignStmt{
|
||||||
Lhs: []ast.Expr{ast.NewIdent("result")},
|
Lhs: []ast.Expr{ast.NewIdent("result")},
|
||||||
Tok: token.DEFINE,
|
Tok: token.DEFINE,
|
||||||
Rhs: []ast.Expr{mustCall(&ast.CallExpr{
|
Rhs: []ast.Expr{&ast.CallExpr{
|
||||||
Fun: &ast.SelectorExpr{X: dbDB, Sel: ast.NewIdent("NamedExec")},
|
Fun: ast.NewIdent("Must"),
|
||||||
Args: []ast.Expr{&ast.BasicLit{Kind: token.STRING, Value: "`" + updateStmt + "`"}, ast.NewIdent(tbl.VarName)},
|
Args: []ast.Expr{&ast.CallExpr{
|
||||||
})},
|
Fun: &ast.SelectorExpr{X: ast.NewIdent("db.DB"), Sel: ast.NewIdent("NamedExec")},
|
||||||
|
Args: []ast.Expr{&ast.BasicLit{Kind: token.STRING, Value: "`" + updateStmt + "`"}, ast.NewIdent(tbl.VarName)},
|
||||||
|
}},
|
||||||
|
}},
|
||||||
},
|
},
|
||||||
|
|
||||||
&ast.IfStmt{
|
&ast.IfStmt{
|
||||||
Cond: &ast.BinaryExpr{
|
Cond: &ast.BinaryExpr{
|
||||||
X: mustCall(&ast.CallExpr{
|
X: &ast.CallExpr{
|
||||||
Fun: &ast.SelectorExpr{X: ast.NewIdent("result"), Sel: ast.NewIdent("RowsAffected")},
|
Fun: ast.NewIdent("Must"),
|
||||||
Args: []ast.Expr{},
|
Args: []ast.Expr{&ast.CallExpr{
|
||||||
}),
|
Fun: &ast.SelectorExpr{X: ast.NewIdent("result"), Sel: ast.NewIdent("RowsAffected")},
|
||||||
|
Args: []ast.Expr{},
|
||||||
|
}},
|
||||||
|
},
|
||||||
Op: token.NEQ,
|
Op: token.NEQ,
|
||||||
Y: &ast.BasicLit{Kind: token.INT, Value: "1"},
|
Y: &ast.BasicLit{Kind: token.INT, Value: "1"},
|
||||||
},
|
},
|
||||||
Body: &ast.BlockStmt{List: []ast.Stmt{&ast.ExprStmt{X: &ast.CallExpr{Fun: ast.NewIdent("panic"), Args: []ast.Expr{&ast.CallExpr{Fun: fmtErrorf, Args: []ast.Expr{&ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf("\"got %s with ID (%%d), so attempted update, but it doesn't exist\"", strings.ToLower(tbl.GoTypeName))}, &ast.SelectorExpr{X: ast.NewIdent(tbl.VarName), Sel: ast.NewIdent("ID")}}}}}}}},
|
Body: &ast.BlockStmt{List: []ast.Stmt{&ast.ExprStmt{X: &ast.CallExpr{Fun: ast.NewIdent("panic"), Args: []ast.Expr{&ast.CallExpr{Fun: ast.NewIdent("fmt.Errorf"), Args: []ast.Expr{&ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf("\"got %s with ID (%%d), so attempted update, but it doesn't exist\"", strings.ToLower(tbl.GoTypeName))}, &ast.SelectorExpr{X: ast.NewIdent(tbl.VarName), Sel: ast.NewIdent("ID")}}}}}}}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -416,7 +409,7 @@ func GenerateSaveItemFunc(tbl schema.Table) *ast.FuncDecl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
funcDecl := &ast.FuncDecl{
|
funcDecl := &ast.FuncDecl{
|
||||||
Recv: dbRecv,
|
Recv: &ast.FieldList{List: []*ast.Field{{Names: []*ast.Ident{ast.NewIdent("db")}, Type: ast.NewIdent("DB")}}},
|
||||||
Name: ast.NewIdent("Save" + tbl.GoTypeName),
|
Name: ast.NewIdent("Save" + tbl.GoTypeName),
|
||||||
Type: &ast.FuncType{
|
Type: &ast.FuncType{
|
||||||
Params: &ast.FieldList{List: []*ast.Field{{Names: []*ast.Ident{ast.NewIdent(tbl.VarName)}, Type: &ast.StarExpr{X: ast.NewIdent(tbl.GoTypeName)}}}},
|
Params: &ast.FieldList{List: []*ast.Field{{Names: []*ast.Ident{ast.NewIdent(tbl.VarName)}, Type: &ast.StarExpr{X: ast.NewIdent(tbl.GoTypeName)}}}},
|
||||||
@ -439,6 +432,7 @@ func getByIDFuncName(tblname string) string {
|
|||||||
// GenerateGetItemByIDFunc produces an AST for the `GetXyzByID()` function.
|
// GenerateGetItemByIDFunc produces an AST for the `GetXyzByID()` function.
|
||||||
// E.g., a table with `table.TypeName = "foods"` will produce a "GetFoodByID()" function.
|
// E.g., a table with `table.TypeName = "foods"` will produce a "GetFoodByID()" function.
|
||||||
func GenerateGetItemByIDFunc(tbl schema.Table) *ast.FuncDecl {
|
func GenerateGetItemByIDFunc(tbl schema.Table) *ast.FuncDecl {
|
||||||
|
recv := &ast.FieldList{List: []*ast.Field{{Names: []*ast.Ident{ast.NewIdent("db")}, Type: ast.NewIdent("DB")}}}
|
||||||
arg := &ast.FieldList{List: []*ast.Field{{Names: []*ast.Ident{ast.NewIdent("id")}, Type: ast.NewIdent(tbl.TypeIDName)}}}
|
arg := &ast.FieldList{List: []*ast.Field{{Names: []*ast.Ident{ast.NewIdent("id")}, Type: ast.NewIdent(tbl.TypeIDName)}}}
|
||||||
result := &ast.FieldList{List: []*ast.Field{{Names: []*ast.Ident{ast.NewIdent("ret")}, Type: ast.NewIdent(tbl.GoTypeName)}, {Names: []*ast.Ident{ast.NewIdent("err")}, Type: ast.NewIdent("error")}}}
|
result := &ast.FieldList{List: []*ast.Field{{Names: []*ast.Ident{ast.NewIdent("ret")}, Type: ast.NewIdent(tbl.GoTypeName)}, {Names: []*ast.Ident{ast.NewIdent("err")}, Type: ast.NewIdent("error")}}}
|
||||||
|
|
||||||
@ -458,7 +452,7 @@ func GenerateGetItemByIDFunc(tbl schema.Table) *ast.FuncDecl {
|
|||||||
&ast.AssignStmt{
|
&ast.AssignStmt{
|
||||||
Lhs: []ast.Expr{ast.NewIdent("err")},
|
Lhs: []ast.Expr{ast.NewIdent("err")},
|
||||||
Tok: token.ASSIGN,
|
Tok: token.ASSIGN,
|
||||||
Rhs: []ast.Expr{&ast.CallExpr{Fun: &ast.SelectorExpr{X: dbDB, Sel: ast.NewIdent("Get")}, Args: []ast.Expr{&ast.UnaryExpr{Op: token.AND, X: ast.NewIdent("ret")}, selectExpr, ast.NewIdent("id")}}},
|
Rhs: []ast.Expr{&ast.CallExpr{Fun: &ast.SelectorExpr{X: ast.NewIdent("db.DB"), Sel: ast.NewIdent("Get")}, Args: []ast.Expr{&ast.UnaryExpr{Op: token.AND, X: ast.NewIdent("ret")}, selectExpr, ast.NewIdent("id")}}},
|
||||||
},
|
},
|
||||||
&ast.IfStmt{
|
&ast.IfStmt{
|
||||||
Cond: &ast.CallExpr{Fun: &ast.SelectorExpr{X: ast.NewIdent("errors"), Sel: ast.NewIdent("Is")}, Args: []ast.Expr{ast.NewIdent("err"), &ast.SelectorExpr{X: ast.NewIdent("sql"), Sel: ast.NewIdent("ErrNoRows")}}},
|
Cond: &ast.CallExpr{Fun: &ast.SelectorExpr{X: ast.NewIdent("errors"), Sel: ast.NewIdent("Is")}, Args: []ast.Expr{ast.NewIdent("err"), &ast.SelectorExpr{X: ast.NewIdent("sql"), Sel: ast.NewIdent("ErrNoRows")}}},
|
||||||
@ -469,7 +463,7 @@ func GenerateGetItemByIDFunc(tbl schema.Table) *ast.FuncDecl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
funcDecl := &ast.FuncDecl{
|
funcDecl := &ast.FuncDecl{
|
||||||
Recv: dbRecv,
|
Recv: recv,
|
||||||
Name: ast.NewIdent(getByIDFuncName(tbl.TableName)),
|
Name: ast.NewIdent(getByIDFuncName(tbl.TableName)),
|
||||||
Type: &ast.FuncType{Params: arg, Results: result},
|
Type: &ast.FuncType{Params: arg, Results: result},
|
||||||
Body: funcBody,
|
Body: funcBody,
|
||||||
@ -481,6 +475,9 @@ func GenerateGetItemByIDFunc(tbl schema.Table) *ast.FuncDecl {
|
|||||||
// E.g., a table with `table.TypeName = "foods"` will produce a "GetAllFoods()" function.
|
// E.g., a table with `table.TypeName = "foods"` will produce a "GetAllFoods()" function.
|
||||||
func GenerateGetAllItemsFunc(tbl schema.Table) *ast.FuncDecl {
|
func GenerateGetAllItemsFunc(tbl schema.Table) *ast.FuncDecl {
|
||||||
funcName := "GetAll" + inflection.Plural(tbl.GoTypeName)
|
funcName := "GetAll" + inflection.Plural(tbl.GoTypeName)
|
||||||
|
recv := &ast.FieldList{List: []*ast.Field{
|
||||||
|
{Names: []*ast.Ident{ast.NewIdent("db")}, Type: ast.NewIdent("DB")},
|
||||||
|
}}
|
||||||
result := &ast.FieldList{List: []*ast.Field{
|
result := &ast.FieldList{List: []*ast.Field{
|
||||||
{Names: []*ast.Ident{ast.NewIdent("ret")}, Type: &ast.ArrayType{Elt: ast.NewIdent(tbl.GoTypeName)}},
|
{Names: []*ast.Ident{ast.NewIdent("ret")}, Type: &ast.ArrayType{Elt: ast.NewIdent(tbl.GoTypeName)}},
|
||||||
}}
|
}}
|
||||||
@ -490,7 +487,7 @@ func GenerateGetAllItemsFunc(tbl schema.Table) *ast.FuncDecl {
|
|||||||
Args: []ast.Expr{
|
Args: []ast.Expr{
|
||||||
&ast.CallExpr{
|
&ast.CallExpr{
|
||||||
Fun: &ast.SelectorExpr{
|
Fun: &ast.SelectorExpr{
|
||||||
X: dbDB,
|
X: ast.NewIdent("db.DB"),
|
||||||
Sel: ast.NewIdent("Select"),
|
Sel: ast.NewIdent("Select"),
|
||||||
},
|
},
|
||||||
Args: []ast.Expr{
|
Args: []ast.Expr{
|
||||||
@ -517,7 +514,7 @@ func GenerateGetAllItemsFunc(tbl schema.Table) *ast.FuncDecl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return &ast.FuncDecl{
|
return &ast.FuncDecl{
|
||||||
Recv: dbRecv,
|
Recv: recv,
|
||||||
Name: ast.NewIdent(funcName),
|
Name: ast.NewIdent(funcName),
|
||||||
Type: &ast.FuncType{
|
Type: &ast.FuncType{
|
||||||
Params: &ast.FieldList{},
|
Params: &ast.FieldList{},
|
||||||
@ -531,6 +528,7 @@ func GenerateGetAllItemsFunc(tbl schema.Table) *ast.FuncDecl {
|
|||||||
// E.g., a table with `table.TypeName = "foods"` will produce a "DeleteFood()" function.
|
// E.g., a table with `table.TypeName = "foods"` will produce a "DeleteFood()" function.
|
||||||
func GenerateDeleteItemFunc(tbl schema.Table) *ast.FuncDecl {
|
func GenerateDeleteItemFunc(tbl schema.Table) *ast.FuncDecl {
|
||||||
funcName := "Delete" + tbl.GoTypeName
|
funcName := "Delete" + tbl.GoTypeName
|
||||||
|
recv := &ast.FieldList{List: []*ast.Field{{Names: []*ast.Ident{ast.NewIdent("db")}, Type: ast.NewIdent("DB")}}}
|
||||||
arg := &ast.FieldList{List: []*ast.Field{{Names: []*ast.Ident{ast.NewIdent(tbl.VarName)}, Type: ast.NewIdent(tbl.GoTypeName)}}}
|
arg := &ast.FieldList{List: []*ast.Field{{Names: []*ast.Ident{ast.NewIdent(tbl.VarName)}, Type: ast.NewIdent(tbl.GoTypeName)}}}
|
||||||
|
|
||||||
funcBody := &ast.BlockStmt{
|
funcBody := &ast.BlockStmt{
|
||||||
@ -538,19 +536,25 @@ func GenerateDeleteItemFunc(tbl schema.Table) *ast.FuncDecl {
|
|||||||
&ast.AssignStmt{
|
&ast.AssignStmt{
|
||||||
Lhs: []ast.Expr{ast.NewIdent("result")},
|
Lhs: []ast.Expr{ast.NewIdent("result")},
|
||||||
Tok: token.DEFINE,
|
Tok: token.DEFINE,
|
||||||
Rhs: []ast.Expr{mustCall(&ast.CallExpr{
|
Rhs: []ast.Expr{&ast.CallExpr{
|
||||||
Fun: &ast.SelectorExpr{X: dbDB, Sel: ast.NewIdent("Exec")},
|
Fun: ast.NewIdent("Must"),
|
||||||
Args: []ast.Expr{
|
Args: []ast.Expr{&ast.CallExpr{
|
||||||
&ast.BasicLit{Kind: token.STRING, Value: "`delete from " + tbl.TableName + " where rowid = ?`"},
|
Fun: &ast.SelectorExpr{X: ast.NewIdent("db.DB"), Sel: ast.NewIdent("Exec")},
|
||||||
&ast.SelectorExpr{X: ast.NewIdent(tbl.VarName), Sel: ast.NewIdent("ID")},
|
Args: []ast.Expr{
|
||||||
},
|
&ast.BasicLit{Kind: token.STRING, Value: "`delete from " + tbl.TableName + " where rowid = ?`"},
|
||||||
})},
|
&ast.SelectorExpr{X: ast.NewIdent(tbl.VarName), Sel: ast.NewIdent("ID")},
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
}},
|
||||||
},
|
},
|
||||||
&ast.IfStmt{
|
&ast.IfStmt{
|
||||||
Cond: &ast.BinaryExpr{
|
Cond: &ast.BinaryExpr{
|
||||||
X: mustCall(
|
X: &ast.CallExpr{
|
||||||
&ast.CallExpr{Fun: &ast.SelectorExpr{X: ast.NewIdent("result"), Sel: ast.NewIdent("RowsAffected")}, Args: []ast.Expr{}},
|
Fun: ast.NewIdent("Must"),
|
||||||
),
|
Args: []ast.Expr{
|
||||||
|
&ast.CallExpr{Fun: &ast.SelectorExpr{X: ast.NewIdent("result"), Sel: ast.NewIdent("RowsAffected")}, Args: []ast.Expr{}},
|
||||||
|
},
|
||||||
|
},
|
||||||
Op: token.NEQ,
|
Op: token.NEQ,
|
||||||
Y: &ast.BasicLit{Kind: token.INT, Value: "1"},
|
Y: &ast.BasicLit{Kind: token.INT, Value: "1"},
|
||||||
},
|
},
|
||||||
@ -558,7 +562,7 @@ func GenerateDeleteItemFunc(tbl schema.Table) *ast.FuncDecl {
|
|||||||
&ast.ExprStmt{X: &ast.CallExpr{
|
&ast.ExprStmt{X: &ast.CallExpr{
|
||||||
Fun: ast.NewIdent("panic"),
|
Fun: ast.NewIdent("panic"),
|
||||||
Args: []ast.Expr{&ast.CallExpr{
|
Args: []ast.Expr{&ast.CallExpr{
|
||||||
Fun: fmtErrorf,
|
Fun: ast.NewIdent("fmt.Errorf"),
|
||||||
Args: []ast.Expr{
|
Args: []ast.Expr{
|
||||||
&ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf("\"tried to delete %s with ID (%%d) but it doesn't exist\"", strings.ToLower(tbl.GoTypeName))},
|
&ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf("\"tried to delete %s with ID (%%d) but it doesn't exist\"", strings.ToLower(tbl.GoTypeName))},
|
||||||
&ast.SelectorExpr{X: ast.NewIdent(tbl.VarName), Sel: ast.NewIdent("ID")},
|
&ast.SelectorExpr{X: ast.NewIdent(tbl.VarName), Sel: ast.NewIdent("ID")},
|
||||||
@ -571,7 +575,7 @@ func GenerateDeleteItemFunc(tbl schema.Table) *ast.FuncDecl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
funcDecl := &ast.FuncDecl{
|
funcDecl := &ast.FuncDecl{
|
||||||
Recv: dbRecv,
|
Recv: recv,
|
||||||
Name: ast.NewIdent(funcName),
|
Name: ast.NewIdent(funcName),
|
||||||
Type: &ast.FuncType{Params: arg, Results: nil},
|
Type: &ast.FuncType{Params: arg, Results: nil},
|
||||||
Body: funcBody,
|
Body: funcBody,
|
||||||
@ -602,3 +606,11 @@ func GenerateSQLFieldsConst(tbl schema.Table) *ast.GenDecl {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------------
|
||||||
|
// Helpers
|
||||||
|
// ---------------
|
||||||
|
|
||||||
|
func SQLFieldsConstIdent(tbl schema.Table) *ast.Ident {
|
||||||
|
return ast.NewIdent(strings.ToLower(tbl.GoTypeName) + "SQLFields")
|
||||||
|
}
|
||||||
|
|||||||
@ -61,16 +61,19 @@ func GenerateModelTestAST(tbl schema.Table, gomodName string) *ast.File {
|
|||||||
&ast.AssignStmt{
|
&ast.AssignStmt{
|
||||||
Lhs: []ast.Expr{ast.NewIdent("db")},
|
Lhs: []ast.Expr{ast.NewIdent("db")},
|
||||||
Tok: token.DEFINE,
|
Tok: token.DEFINE,
|
||||||
Rhs: []ast.Expr{mustCall(&ast.CallExpr{
|
Rhs: []ast.Expr{&ast.CallExpr{
|
||||||
Fun: ast.NewIdent("Create"),
|
Fun: ast.NewIdent("Must"),
|
||||||
Args: []ast.Expr{&ast.CallExpr{
|
Args: []ast.Expr{&ast.CallExpr{
|
||||||
Fun: ast.NewIdent("fmt.Sprintf"),
|
Fun: ast.NewIdent("Create"),
|
||||||
Args: []ast.Expr{
|
Args: []ast.Expr{&ast.CallExpr{
|
||||||
&ast.BasicLit{Kind: token.STRING, Value: `"file:%s?mode=memory&cache=shared"`},
|
Fun: ast.NewIdent("fmt.Sprintf"),
|
||||||
ast.NewIdent("dbName"),
|
Args: []ast.Expr{
|
||||||
},
|
&ast.BasicLit{Kind: token.STRING, Value: `"file:%s?mode=memory&cache=shared"`},
|
||||||
|
ast.NewIdent("dbName"),
|
||||||
|
},
|
||||||
|
}},
|
||||||
}},
|
}},
|
||||||
})},
|
}},
|
||||||
},
|
},
|
||||||
// return db
|
// return db
|
||||||
&ast.ReturnStmt{
|
&ast.ReturnStmt{
|
||||||
@ -110,8 +113,6 @@ func GenerateModelTestAST(tbl schema.Table, gomodName string) *ast.File {
|
|||||||
description1 := `"an item"`
|
description1 := `"an item"`
|
||||||
description2 := `"a big item"`
|
description2 := `"a big item"`
|
||||||
|
|
||||||
hasCreatedAt, hasUpdatedAt := tbl.HasAutoTimestamps()
|
|
||||||
|
|
||||||
testCreateUpdateDelete := &ast.FuncDecl{
|
testCreateUpdateDelete := &ast.FuncDecl{
|
||||||
Name: ast.NewIdent("TestCreateUpdateDelete" + tbl.GoTypeName),
|
Name: ast.NewIdent("TestCreateUpdateDelete" + tbl.GoTypeName),
|
||||||
Type: &ast.FuncType{
|
Type: &ast.FuncType{
|
||||||
@ -123,144 +124,119 @@ func GenerateModelTestAST(tbl schema.Table, gomodName string) *ast.File {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
Body: &ast.BlockStmt{
|
Body: &ast.BlockStmt{
|
||||||
List: func() []ast.Stmt {
|
List: []ast.Stmt{
|
||||||
assertNotZero := func(obj *ast.Ident, field string) *ast.ExprStmt {
|
// item := Item{Description: "an item"}
|
||||||
return &ast.ExprStmt{X: &ast.CallExpr{
|
&ast.AssignStmt{
|
||||||
Fun: ast.NewIdent("assert.NotZero"),
|
Lhs: []ast.Expr{testObj},
|
||||||
Args: []ast.Expr{ast.NewIdent("t"), &ast.SelectorExpr{X: obj, Sel: ast.NewIdent(field)}},
|
Tok: token.DEFINE,
|
||||||
}}
|
Rhs: []ast.Expr{&ast.CompositeLit{
|
||||||
}
|
Type: ast.NewIdent(tbl.GoTypeName),
|
||||||
|
Elts: []ast.Expr{
|
||||||
stmts := []ast.Stmt{
|
&ast.KeyValueExpr{
|
||||||
// item := Item{Description: "an item"}
|
Key: fieldName,
|
||||||
&ast.AssignStmt{
|
Value: &ast.BasicLit{Kind: token.STRING, Value: description1},
|
||||||
Lhs: []ast.Expr{testObj},
|
|
||||||
Tok: token.DEFINE,
|
|
||||||
Rhs: []ast.Expr{&ast.CompositeLit{
|
|
||||||
Type: ast.NewIdent(tbl.GoTypeName),
|
|
||||||
Elts: []ast.Expr{
|
|
||||||
&ast.KeyValueExpr{
|
|
||||||
Key: fieldName,
|
|
||||||
Value: &ast.BasicLit{Kind: token.STRING, Value: description1},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}},
|
|
||||||
},
|
|
||||||
|
|
||||||
// TestDB.SaveItem(&item)
|
|
||||||
&ast.ExprStmt{X: &ast.CallExpr{
|
|
||||||
Fun: ast.NewIdent("TestDB.Save" + tbl.GoTypeName),
|
|
||||||
Args: []ast.Expr{&ast.UnaryExpr{Op: token.AND, X: testObj}},
|
|
||||||
}},
|
|
||||||
|
|
||||||
// require.NotZero(t, item.ID)
|
|
||||||
&ast.ExprStmt{X: &ast.CallExpr{
|
|
||||||
Fun: ast.NewIdent("require.NotZero"),
|
|
||||||
Args: []ast.Expr{ast.NewIdent("t"), &ast.SelectorExpr{X: testObj, Sel: ast.NewIdent("ID")}},
|
|
||||||
}},
|
|
||||||
}
|
|
||||||
|
|
||||||
// After create: assert timestamps are set
|
|
||||||
if hasCreatedAt {
|
|
||||||
stmts = append(stmts, assertNotZero(testObj, "CreatedAt"))
|
|
||||||
}
|
|
||||||
if hasUpdatedAt {
|
|
||||||
stmts = append(stmts, assertNotZero(testObj, "UpdatedAt"))
|
|
||||||
}
|
|
||||||
|
|
||||||
stmts = append(stmts,
|
|
||||||
// item2 := Must(TestDB.GetItemByID(item.ID))
|
|
||||||
&ast.AssignStmt{
|
|
||||||
Lhs: []ast.Expr{testObj2},
|
|
||||||
Tok: token.DEFINE,
|
|
||||||
Rhs: []ast.Expr{mustCall(&ast.CallExpr{
|
|
||||||
Fun: ast.NewIdent("TestDB.Get" + tbl.GoTypeName + "ByID"),
|
|
||||||
Args: []ast.Expr{&ast.SelectorExpr{X: testObj, Sel: ast.NewIdent("ID")}},
|
|
||||||
})},
|
|
||||||
},
|
|
||||||
|
|
||||||
// assert.Equal(t, "an item", item2.Description)
|
|
||||||
&ast.ExprStmt{X: &ast.CallExpr{
|
|
||||||
Fun: ast.NewIdent("assert.Equal"),
|
|
||||||
Args: []ast.Expr{
|
|
||||||
ast.NewIdent("t"),
|
|
||||||
&ast.BasicLit{Kind: token.STRING, Value: description1},
|
|
||||||
&ast.SelectorExpr{X: testObj2, Sel: fieldName},
|
|
||||||
},
|
},
|
||||||
}},
|
}},
|
||||||
|
},
|
||||||
|
|
||||||
// item.Description = "a big item"
|
// TestDB.SaveItem(&item)
|
||||||
&ast.AssignStmt{
|
&ast.ExprStmt{X: &ast.CallExpr{
|
||||||
Lhs: []ast.Expr{&ast.SelectorExpr{X: testObj, Sel: fieldName}},
|
Fun: ast.NewIdent("TestDB.Save" + tbl.GoTypeName),
|
||||||
Tok: token.ASSIGN,
|
Args: []ast.Expr{&ast.UnaryExpr{Op: token.AND, X: testObj}},
|
||||||
Rhs: []ast.Expr{&ast.BasicLit{Kind: token.STRING, Value: description2}},
|
}},
|
||||||
},
|
|
||||||
|
|
||||||
// TestDB.SaveItem(&item)
|
// require.NotZero(t, item.ID)
|
||||||
&ast.ExprStmt{X: &ast.CallExpr{
|
&ast.ExprStmt{X: &ast.CallExpr{
|
||||||
Fun: ast.NewIdent("TestDB.Save" + tbl.GoTypeName),
|
Fun: ast.NewIdent("require.NotZero"),
|
||||||
Args: []ast.Expr{&ast.UnaryExpr{Op: token.AND, X: testObj}},
|
Args: []ast.Expr{ast.NewIdent("t"), &ast.SelectorExpr{X: testObj, Sel: ast.NewIdent("ID")}},
|
||||||
}},
|
}},
|
||||||
)
|
|
||||||
|
|
||||||
// After update: assert timestamps are still set
|
// item2 := Must(TestDB.GetItemByID(item.ID))
|
||||||
if hasCreatedAt {
|
&ast.AssignStmt{
|
||||||
stmts = append(stmts, assertNotZero(testObj, "CreatedAt"))
|
Lhs: []ast.Expr{testObj2},
|
||||||
}
|
Tok: token.DEFINE,
|
||||||
if hasUpdatedAt {
|
Rhs: []ast.Expr{&ast.CallExpr{
|
||||||
stmts = append(stmts, assertNotZero(testObj, "UpdatedAt"))
|
Fun: ast.NewIdent("Must"),
|
||||||
}
|
Args: []ast.Expr{&ast.CallExpr{
|
||||||
|
|
||||||
stmts = append(stmts,
|
|
||||||
// item2 = Must(TestDB.GetItemByID(item.ID))
|
|
||||||
&ast.AssignStmt{
|
|
||||||
Lhs: []ast.Expr{testObj2},
|
|
||||||
Tok: token.ASSIGN,
|
|
||||||
Rhs: []ast.Expr{mustCall(&ast.CallExpr{
|
|
||||||
Fun: ast.NewIdent("TestDB.Get" + tbl.GoTypeName + "ByID"),
|
|
||||||
Args: []ast.Expr{&ast.SelectorExpr{X: testObj, Sel: ast.NewIdent("ID")}},
|
|
||||||
})},
|
|
||||||
},
|
|
||||||
|
|
||||||
// assert.Equal(t, item.Description, item2.Description)
|
|
||||||
&ast.ExprStmt{X: &ast.CallExpr{
|
|
||||||
Fun: ast.NewIdent("assert.Equal"),
|
|
||||||
Args: []ast.Expr{
|
|
||||||
ast.NewIdent("t"),
|
|
||||||
&ast.SelectorExpr{X: testObj, Sel: fieldName},
|
|
||||||
&ast.SelectorExpr{X: testObj2, Sel: fieldName},
|
|
||||||
},
|
|
||||||
}},
|
|
||||||
|
|
||||||
// TestDB.DeleteItem(item)
|
|
||||||
&ast.ExprStmt{X: &ast.CallExpr{
|
|
||||||
Fun: ast.NewIdent("TestDB.Delete" + tbl.GoTypeName),
|
|
||||||
Args: []ast.Expr{testObj},
|
|
||||||
}},
|
|
||||||
|
|
||||||
// _, err := TestDB.GetItemByID(item.ID)
|
|
||||||
&ast.AssignStmt{
|
|
||||||
Lhs: []ast.Expr{ast.NewIdent("_"), ast.NewIdent("err")},
|
|
||||||
Tok: token.DEFINE,
|
|
||||||
Rhs: []ast.Expr{&ast.CallExpr{
|
|
||||||
Fun: ast.NewIdent("TestDB.Get" + tbl.GoTypeName + "ByID"),
|
Fun: ast.NewIdent("TestDB.Get" + tbl.GoTypeName + "ByID"),
|
||||||
Args: []ast.Expr{&ast.SelectorExpr{X: testObj, Sel: ast.NewIdent("ID")}},
|
Args: []ast.Expr{&ast.SelectorExpr{X: testObj, Sel: ast.NewIdent("ID")}},
|
||||||
}},
|
}},
|
||||||
},
|
|
||||||
|
|
||||||
// assert.ErrorIs(t, err, db.ErrNotInDB)
|
|
||||||
&ast.ExprStmt{X: &ast.CallExpr{
|
|
||||||
Fun: ast.NewIdent("assert.ErrorIs"),
|
|
||||||
Args: []ast.Expr{
|
|
||||||
ast.NewIdent("t"),
|
|
||||||
ast.NewIdent("err"),
|
|
||||||
ast.NewIdent("ErrNotInDB"),
|
|
||||||
},
|
|
||||||
}},
|
}},
|
||||||
)
|
},
|
||||||
|
|
||||||
return stmts
|
// assert.Equal(t, "an item", item2.Description)
|
||||||
}(),
|
&ast.ExprStmt{X: &ast.CallExpr{
|
||||||
|
Fun: ast.NewIdent("assert.Equal"),
|
||||||
|
Args: []ast.Expr{
|
||||||
|
ast.NewIdent("t"),
|
||||||
|
&ast.BasicLit{Kind: token.STRING, Value: description1},
|
||||||
|
&ast.SelectorExpr{X: testObj2, Sel: fieldName},
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
|
||||||
|
// item.Description = "a big item"
|
||||||
|
&ast.AssignStmt{
|
||||||
|
Lhs: []ast.Expr{&ast.SelectorExpr{X: testObj, Sel: fieldName}},
|
||||||
|
Tok: token.ASSIGN,
|
||||||
|
Rhs: []ast.Expr{&ast.BasicLit{Kind: token.STRING, Value: description2}},
|
||||||
|
},
|
||||||
|
|
||||||
|
// TestDB.SaveItem(&item)
|
||||||
|
&ast.ExprStmt{X: &ast.CallExpr{
|
||||||
|
Fun: ast.NewIdent("TestDB.Save" + tbl.GoTypeName),
|
||||||
|
Args: []ast.Expr{&ast.UnaryExpr{Op: token.AND, X: testObj}},
|
||||||
|
}},
|
||||||
|
|
||||||
|
// item2 = Must(TestDB.GetItemByID(item.ID))
|
||||||
|
&ast.AssignStmt{
|
||||||
|
Lhs: []ast.Expr{testObj2},
|
||||||
|
Tok: token.ASSIGN,
|
||||||
|
Rhs: []ast.Expr{&ast.CallExpr{
|
||||||
|
Fun: ast.NewIdent("Must"),
|
||||||
|
Args: []ast.Expr{&ast.CallExpr{
|
||||||
|
Fun: ast.NewIdent("TestDB.Get" + tbl.GoTypeName + "ByID"),
|
||||||
|
Args: []ast.Expr{&ast.SelectorExpr{X: testObj, Sel: ast.NewIdent("ID")}},
|
||||||
|
}},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
|
||||||
|
// assert.Equal(t, item.Description, item2.Description)
|
||||||
|
&ast.ExprStmt{X: &ast.CallExpr{
|
||||||
|
Fun: ast.NewIdent("assert.Equal"),
|
||||||
|
Args: []ast.Expr{
|
||||||
|
ast.NewIdent("t"),
|
||||||
|
&ast.SelectorExpr{X: testObj, Sel: fieldName},
|
||||||
|
&ast.SelectorExpr{X: testObj2, Sel: fieldName},
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
|
||||||
|
// TestDB.DeleteItem(item)
|
||||||
|
&ast.ExprStmt{X: &ast.CallExpr{
|
||||||
|
Fun: ast.NewIdent("TestDB.Delete" + tbl.GoTypeName),
|
||||||
|
Args: []ast.Expr{testObj},
|
||||||
|
}},
|
||||||
|
|
||||||
|
// _, err := TestDB.GetItemByID(item.ID)
|
||||||
|
&ast.AssignStmt{
|
||||||
|
Lhs: []ast.Expr{ast.NewIdent("_"), ast.NewIdent("err")},
|
||||||
|
Tok: token.DEFINE,
|
||||||
|
Rhs: []ast.Expr{&ast.CallExpr{
|
||||||
|
Fun: ast.NewIdent("TestDB.Get" + tbl.GoTypeName + "ByID"),
|
||||||
|
Args: []ast.Expr{&ast.SelectorExpr{X: testObj, Sel: ast.NewIdent("ID")}},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
|
||||||
|
// assert.ErrorIs(t, err, db.ErrNotInDB)
|
||||||
|
&ast.ExprStmt{X: &ast.CallExpr{
|
||||||
|
Fun: ast.NewIdent("assert.ErrorIs"),
|
||||||
|
Args: []ast.Expr{
|
||||||
|
ast.NewIdent("t"),
|
||||||
|
ast.NewIdent("err"),
|
||||||
|
ast.NewIdent("ErrNotInDB"),
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user