Skip to content

Commit

Permalink
feat: Update CRUD generator
Browse files Browse the repository at this point in the history
  • Loading branch information
ginokent committed Aug 12, 2024
1 parent a64c8e7 commit 4371ea7
Show file tree
Hide file tree
Showing 9 changed files with 191 additions and 81 deletions.
37 changes: 19 additions & 18 deletions internal/arcgen/lang/go/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,23 +57,7 @@ func generate(arcSrcSetSlice ARCSourceSetSlice) error {
if config.GenerateGoCRUDPackage() {
crudFileExt := ".crud" + genFileExt

if err := func() error {
filename := filepath.Join(config.GoCRUDPackagePath(), "common"+crudFileExt)
f, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, rw_r__r__)
if err != nil {
return errorz.Errorf("os.OpenFile: %w", err)
}
defer f.Close()

if err := fprintCRUDCommon(f, bytes.NewBuffer(nil), arcSrcSetSlice); err != nil {
return errorz.Errorf("sprint: %w", err)
}

return nil
}(); err != nil {
return errorz.Errorf("f: %w", err)
}

crudFiles := make([]string, 0)
for _, arcSrcSet := range arcSrcSetSlice {
// closure for defer
if err := func() error {
Expand All @@ -84,7 +68,7 @@ func generate(arcSrcSetSlice ARCSourceSetSlice) error {
return errorz.Errorf("os.OpenFile: %w", err)
}
defer f.Close()
f.Name()
crudFiles = append(crudFiles, filename)

if err := fprintCRUD(
f,
Expand All @@ -98,6 +82,23 @@ func generate(arcSrcSetSlice ARCSourceSetSlice) error {
return errorz.Errorf("f: %w", err)
}
}

if err := func() error {
filename := filepath.Join(config.GoCRUDPackagePath(), "common"+crudFileExt)
f, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, rw_r__r__)
if err != nil {
return errorz.Errorf("os.OpenFile: %w", err)
}
defer f.Close()

if err := fprintCRUDCommon(f, bytes.NewBuffer(nil), arcSrcSetSlice, crudFiles); err != nil {
return errorz.Errorf("sprint: %w", err)
}

return nil
}(); err != nil {
return errorz.Errorf("f: %w", err)
}
}

return nil
Expand Down
105 changes: 82 additions & 23 deletions internal/arcgen/lang/go/generate_crud_common.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,22 @@ package arcgengo

import (
"go/ast"
"go/parser"
"go/printer"
"go/token"
"io"
"path/filepath"
"strconv"
"strings"

errorz "github.com/kunitsucom/util.go/errors"

"github.com/kunitsucom/arcgen/internal/arcgen/lang/util"
"github.com/kunitsucom/arcgen/internal/config"
)

func fprintCRUDCommon(osFile osFile, buf buffer, arcSrcSetSlice ARCSourceSetSlice) error {
content, err := generateCRUDCommonFileContent(buf, arcSrcSetSlice)
func fprintCRUDCommon(osFile osFile, buf buffer, arcSrcSetSlice ARCSourceSetSlice, crudFiles []string) error {
content, err := generateCRUDCommonFileContent(buf, arcSrcSetSlice, crudFiles)
if err != nil {
return errorz.Errorf("generateCRUDCommonFileContent: %w", err)
}
Expand All @@ -27,8 +30,13 @@ func fprintCRUDCommon(osFile osFile, buf buffer, arcSrcSetSlice ARCSourceSetSlic
return nil
}

const (
sqlQueryerContextVarName = "sqlContext"
sqlQueryerContextTypeName = "sqlQueryerContext"
)

//nolint:funlen
func generateCRUDCommonFileContent(buf buffer, _ ARCSourceSetSlice) (string, error) {
func generateCRUDCommonFileContent(buf buffer, arcSrcSetSlice ARCSourceSetSlice, crudFiles []string) (string, error) {

Check failure on line 39 in internal/arcgen/lang/go/generate_crud_common.go

View workflow job for this annotation

GitHub Actions / go-lint

cognitive complexity 31 of func `generateCRUDCommonFileContent` is high (> 30) (gocognit)
astFile := &ast.File{
// package
Name: &ast.Ident{
Expand All @@ -38,13 +46,13 @@ func generateCRUDCommonFileContent(buf buffer, _ ARCSourceSetSlice) (string, err
Decls: []ast.Decl{},
}

// // Since all directories are the same from arcSrcSetSlice[0].Filename to arcSrcSetSlice[len(-1)].Filename,
// // get the package path from arcSrcSetSlice[0].Filename.
// dir := filepath.Dir(arcSrcSetSlice[0].Filename)
// structPackagePath, err := util.GetPackagePath(dir)
// if err != nil {
// return "", errorz.Errorf("GetPackagePath: %w", err)
// }
// Since all directories are the same from arcSrcSetSlice[0].Filename to arcSrcSetSlice[len(-1)].Filename,
// get the package path from arcSrcSetSlice[0].Filename.
dir := filepath.Dir(arcSrcSetSlice[0].Filename)
structPackagePath, err := util.GetPackagePath(dir)
if err != nil {
return "", errorz.Errorf("GetPackagePath: %w", err)
}

astFile.Decls = append(astFile.Decls,
// import (
Expand All @@ -62,15 +70,15 @@ func generateCRUDCommonFileContent(buf buffer, _ ARCSourceSetSlice) (string, err
&ast.ImportSpec{
Path: &ast.BasicLit{Kind: token.STRING, Value: strconv.Quote("database/sql")},
},
// &ast.ImportSpec{
// Name: &ast.Ident{Name: "dao"},
// Path: &ast.BasicLit{Kind: token.STRING, Value: strconv.Quote(structPackagePath)},
// },
&ast.ImportSpec{
Name: &ast.Ident{Name: "dao"},
Path: &ast.BasicLit{Kind: token.STRING, Value: strconv.Quote(structPackagePath)},
},
},
},
)

// type sqlContext interface {
// type sqlQueryerContext interface {
// QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error)
// QueryRowContext(ctx context.Context, query string, args ...interface{}) *sql.Row
// ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error)
Expand All @@ -80,7 +88,8 @@ func generateCRUDCommonFileContent(buf buffer, _ ARCSourceSetSlice) (string, err
Tok: token.TYPE,
Specs: []ast.Spec{
&ast.TypeSpec{
Name: &ast.Ident{Name: "sqlContext"},
// Assign: token.Pos(1),
Name: &ast.Ident{Name: sqlQueryerContextTypeName},
Type: &ast.InterfaceType{
Methods: &ast.FieldList{
List: []*ast.Field{
Expand Down Expand Up @@ -133,27 +142,77 @@ func generateCRUDCommonFileContent(buf buffer, _ ARCSourceSetSlice) (string, err
},
)

// type Queryer struct {}
// type _CRUD struct {}
astFile.Decls = append(astFile.Decls,
&ast.GenDecl{
Tok: token.TYPE,
Specs: []ast.Spec{
&ast.TypeSpec{
Name: &ast.Ident{Name: "Queryer"},
Name: &ast.Ident{Name: config.GoCRUDTypeNameUnexported()},
Type: &ast.StructType{Fields: &ast.FieldList{}},
},
},
},
)

// func NewQueryer() *Query {
// return &Queryer{}
// type CRUD interface {
// Create{StructName}(ctx context.Context, sqlQueryer sqlQueryerContext, s *{Struct}) error
// ...
// }
methods := make([]*ast.Field, 0)
fset := token.NewFileSet()
for _, crudFile := range crudFiles {
rootNode, err := parser.ParseFile(fset, crudFile, nil, parser.ParseComments)
if err != nil {
// MEMO: parser.ParseFile err contains file path, so no need to log it
return "", errorz.Errorf("parser.ParseFile: %w", err)
}

// MEMO: Inspect is used to get the method declaration from the file
ast.Inspect(rootNode, func(node ast.Node) bool {
switch n := node.(type) {
case *ast.FuncDecl:
//nolint:nestif
if n.Recv != nil && len(n.Recv.List) > 0 {
if t, ok := n.Recv.List[0].Type.(*ast.StarExpr); ok {
if ident, ok := t.X.(*ast.Ident); ok {
if ident.Name == config.GoCRUDTypeNameUnexported() {
methods = append(methods, &ast.Field{
Names: []*ast.Ident{{Name: n.Name.Name}},
Type: n.Type,
})
}
}
}
}
default:
// noop
}
return true
})
}
astFile.Decls = append(astFile.Decls,
&ast.GenDecl{
Tok: token.TYPE,
Specs: []ast.Spec{
&ast.TypeSpec{
Name: &ast.Ident{Name: config.GoCRUDTypeName()},
Type: &ast.InterfaceType{
Methods: &ast.FieldList{List: methods},
},
},
},
},
)

// func NewCRUD() CRUD {
// return &_CRUD{}
// }
astFile.Decls = append(astFile.Decls,
&ast.FuncDecl{
Name: &ast.Ident{Name: "NewQueryer"},
Type: &ast.FuncType{Results: &ast.FieldList{List: []*ast.Field{{Type: &ast.StarExpr{X: &ast.Ident{Name: "Queryer"}}}}}},
Body: &ast.BlockStmt{List: []ast.Stmt{&ast.ReturnStmt{Results: []ast.Expr{&ast.UnaryExpr{Op: token.AND, X: &ast.Ident{Name: "Queryer{}"}}}}}},
Name: &ast.Ident{Name: "New" + config.GoCRUDTypeName()},
Type: &ast.FuncType{Results: &ast.FieldList{List: []*ast.Field{{Type: &ast.Ident{Name: config.GoCRUDTypeName()}}}}},
Body: &ast.BlockStmt{List: []ast.Stmt{&ast.ReturnStmt{Results: []ast.Expr{&ast.UnaryExpr{Op: token.AND, X: &ast.Ident{Name: config.GoCRUDTypeNameUnexported() + "{}"}}}}}},
},
)

Expand Down
12 changes: 7 additions & 5 deletions internal/arcgen/lang/go/generate_crud_create.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import (
"go/token"
"strconv"
"strings"

"github.com/kunitsucom/arcgen/internal/config"
)

//nolint:funlen
Expand All @@ -17,7 +19,7 @@ func generateCREATEContent(astFile *ast.File, arcSrcSet *ARCSourceSet) {

// const Create{StructName}Query = `INSERT INTO {table_name} ({column_name1}, {column_name2}) VALUES (?, ?)`
//
// func (q *query) Create{StructName}(ctx context.Context, queryer sqlContext, s *{Struct}) error {
// func (q *query) Create{StructName}(ctx context.Context, queryer sqlQueryerContext, s *{Struct}) error {
// if _, err := queryer.ExecContext(ctx, Create{StructName}Query, s.{ColumnName1}, s.{ColumnName2}); err != nil {
// return fmt.Errorf("q.queryer.ExecContext: %w", err)
// }
Expand All @@ -39,12 +41,12 @@ func generateCREATEContent(astFile *ast.File, arcSrcSet *ARCSourceSet) {
},
},
&ast.FuncDecl{
Recv: &ast.FieldList{List: []*ast.Field{{Names: []*ast.Ident{{Name: "q"}}, Type: &ast.StarExpr{X: &ast.Ident{Name: "Queryer"}}}}},
Recv: &ast.FieldList{List: []*ast.Field{{Names: []*ast.Ident{{Name: "q"}}, Type: &ast.StarExpr{X: &ast.Ident{Name: config.GoCRUDTypeNameUnexported()}}}}},
Name: &ast.Ident{Name: funcName},
Type: &ast.FuncType{
Params: &ast.FieldList{List: []*ast.Field{
{Names: []*ast.Ident{{Name: "ctx"}}, Type: &ast.Ident{Name: "context.Context"}},
{Names: []*ast.Ident{{Name: "sqlCtx"}}, Type: &ast.Ident{Name: "sqlContext"}},
{Names: []*ast.Ident{{Name: sqlQueryerContextVarName}}, Type: &ast.Ident{Name: sqlQueryerContextTypeName}},
{Names: []*ast.Ident{{Name: "s"}}, Type: &ast.StarExpr{X: &ast.Ident{Name: "dao." + structName}}},
}},
Results: &ast.FieldList{List: []*ast.Field{
Expand All @@ -54,13 +56,13 @@ func generateCREATEContent(astFile *ast.File, arcSrcSet *ARCSourceSet) {
Body: &ast.BlockStmt{
List: []ast.Stmt{
&ast.IfStmt{
// if _, err := queryer.ExecContext(ctx, Create{StructName}Query, s.{ColumnName1}, s.{ColumnName2}); err != nil {
// if _, err := sqlQueryer.ExecContext(ctx, Create{StructName}Query, s.{ColumnName1}, s.{ColumnName2}); err != nil {
Init: &ast.AssignStmt{
Lhs: []ast.Expr{&ast.Ident{Name: "_"}, &ast.Ident{Name: "err"}},
Tok: token.DEFINE,
Rhs: []ast.Expr{&ast.CallExpr{
Fun: &ast.SelectorExpr{
X: &ast.Ident{Name: "sqlCtx"},
X: &ast.Ident{Name: sqlQueryerContextVarName},
Sel: &ast.Ident{Name: "ExecContext"},
},
Args: append(
Expand Down
15 changes: 9 additions & 6 deletions internal/arcgen/lang/go/generate_crud_delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import (
"go/token"
"strconv"
"strings"

"github.com/kunitsucom/arcgen/internal/arcgen/lang/util"
"github.com/kunitsucom/arcgen/internal/config"
)

//nolint:funlen
Expand All @@ -16,7 +19,7 @@ func generateDELETEContent(astFile *ast.File, arcSrcSet *ARCSourceSet) {

// const Delete{StructName}Query = `DELETE FROM {table_name} WHERE {pk1} = ? [AND {pk2} = ?]`
//
// func (q *query) Delete{StructName}(ctx context.Context, queryer sqlContext, pk1 pk1type [, pk2 pk2type]) error {
// func (q *query) Delete{StructName}(ctx context.Context, queryer sqlQueryerContext, pk1 pk1type [, pk2 pk2type]) error {
// if _, err := queryer.ExecContext(ctx, Delete{StructName}Query, pk1 [, pk2]); err != nil {
// return fmt.Errorf("q.queryer.ExecContext: %w", err)
// }
Expand Down Expand Up @@ -45,18 +48,18 @@ func generateDELETEContent(astFile *ast.File, arcSrcSet *ARCSourceSet) {
},
},
&ast.FuncDecl{
Recv: &ast.FieldList{List: []*ast.Field{{Names: []*ast.Ident{{Name: "q"}}, Type: &ast.StarExpr{X: &ast.Ident{Name: "Queryer"}}}}},
Recv: &ast.FieldList{List: []*ast.Field{{Names: []*ast.Ident{{Name: "q"}}, Type: &ast.StarExpr{X: &ast.Ident{Name: config.GoCRUDTypeNameUnexported()}}}}},
Name: &ast.Ident{Name: funcName},
Type: &ast.FuncType{
Params: &ast.FieldList{List: append(
[]*ast.Field{
{Names: []*ast.Ident{{Name: "ctx"}}, Type: &ast.Ident{Name: "context.Context"}},
{Names: []*ast.Ident{{Name: "sqlCtx"}}, Type: &ast.Ident{Name: "sqlContext"}},
{Names: []*ast.Ident{{Name: sqlQueryerContextVarName}}, Type: &ast.Ident{Name: sqlQueryerContextTypeName}},
},
func() []*ast.Field {
var fields []*ast.Field
for _, c := range pkColumns {
fields = append(fields, &ast.Field{Names: []*ast.Ident{{Name: c.ColumnName}}, Type: &ast.Ident{Name: c.FieldType}})
fields = append(fields, &ast.Field{Names: []*ast.Ident{{Name: util.PascalCaseToCamelCase(c.FieldName)}}, Type: &ast.Ident{Name: c.FieldType}})
}
return fields
}()...,
Expand All @@ -74,7 +77,7 @@ func generateDELETEContent(astFile *ast.File, arcSrcSet *ARCSourceSet) {
Tok: token.DEFINE,
Rhs: []ast.Expr{&ast.CallExpr{
Fun: &ast.SelectorExpr{
X: &ast.Ident{Name: "sqlCtx"},
X: &ast.Ident{Name: sqlQueryerContextVarName},
Sel: &ast.Ident{Name: "ExecContext"},
},
Args: append(
Expand All @@ -85,7 +88,7 @@ func generateDELETEContent(astFile *ast.File, arcSrcSet *ARCSourceSet) {
func() []ast.Expr {
var args []ast.Expr
for _, c := range pkColumns {
args = append(args, &ast.Ident{Name: c.ColumnName})
args = append(args, &ast.Ident{Name: util.PascalCaseToCamelCase(c.FieldName)})
}
return args
}()...,
Expand Down
Loading

0 comments on commit 4371ea7

Please sign in to comment.