Skip to content

Commit

Permalink
WIP: feature generate from object (#407)
Browse files Browse the repository at this point in the history
* chore(examples): only generate model

* feat: generate from object

* feat(generate): optimize generated tag

* feat: remove verbose method

* feat: optimize filename

* feat: add FileName on Object
  • Loading branch information
tr1v3r authored Apr 18, 2022
1 parent 2f065f7 commit 2492f01
Show file tree
Hide file tree
Showing 16 changed files with 337 additions and 29 deletions.
78 changes: 78 additions & 0 deletions examples/cmd/from_object/generate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package main

import (
"gorm.io/gen"
"gorm.io/gen/helper"
)

var detail, data helper.Object

func init() {
detail = &Demo{
structName: "Detail",
fileName: "diy_data_detail",
fields: []helper.Field{
&DemoField{
name: "Username",
typ: "string",
jsonTag: "username",
comment: "用户名",
},
&DemoField{
name: "Age",
typ: "uint",
jsonTag: "age",
comment: "用户年龄",
},
&DemoField{
name: "Phone",
typ: "string",
jsonTag: "phone",
comment: "手机号",
},
},
}

data = &Demo{
structName: "Data",
tableName: "data",
fileName: "diy_data",
fields: []helper.Field{
&DemoField{
name: "ID",
typ: "uint",
gormTag: "column:id;type:bigint unsigned;primaryKey;autoIncrement:true",
jsonTag: "id",
tag: `kms:"enc:aes"`,
comment: "主键",
},
&DemoField{
name: "UserInfo",
typ: "[]Detail",
jsonTag: "user_info",
comment: "用户信息",
},
&DemoField{
name: "Remark",
typ: "json.RawMessage",
gormTag: "column:detail",
jsonTag: "remark",
tag: `kms:"enc:aes"`,
comment: "备注\n详细信息",
},
},
}
}

func main() {
g := gen.NewGenerator(gen.Config{
OutPath: "/tmp/gentest/query",
ModelPkgPath: "/tmp/gentest/demo",
})

g.GenerateModelFrom(detail)

g.ApplyBasic(g.GenerateModelFrom(data))

g.Execute()
}
34 changes: 34 additions & 0 deletions examples/cmd/from_object/prepare.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package main

import "gorm.io/gen/helper"

var _ helper.Object = new(Demo)

type Demo struct {
structName string
tableName string
fileName string
fields []helper.Field
}

func (d *Demo) TableName() string { return d.tableName }
func (d *Demo) StructName() string { return d.structName }
func (d *Demo) FileName() string { return d.fileName }
func (d *Demo) ImportPkgPaths() []string { return nil }
func (d *Demo) Fields() []helper.Field { return d.fields }

type DemoField struct {
name string
typ string
gormTag string
jsonTag string
tag string
comment string
}

func (f *DemoField) Name() string { return f.name }
func (f *DemoField) Type() string { return f.typ }
func (f *DemoField) GORMTag() string { return f.gormTag }
func (f *DemoField) JSONTag() string { return f.jsonTag }
func (f *DemoField) Tag() string { return f.tag }
func (f *DemoField) Comment() string { return f.comment }
2 changes: 1 addition & 1 deletion examples/cmd/gen/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
)

func init() {
dal.DB = dal.ConnectDB(conf.MySQLDSN)
dal.DB = dal.ConnectDB(conf.MySQLDSN).Debug()

prepare(dal.DB) // prepare table for generate
}
Expand Down
25 changes: 25 additions & 0 deletions examples/cmd/only_model/generate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package main

import (
"gorm.io/gen"
"gorm.io/gen/examples/conf"
"gorm.io/gen/examples/dal"
)

func init() {
dal.DB = dal.ConnectDB(conf.MySQLDSN).Debug()

prepare(dal.DB) // prepare table for generate
}

func main() {
g := gen.NewGenerator(gen.Config{
OutPath: "/tmp/gentest/query",
})

g.UseDB(dal.DB)

g.GenerateAllTable()

g.Execute()
}
19 changes: 19 additions & 0 deletions examples/cmd/only_model/prepare.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package main

import (
"gorm.io/gorm"
)

// prepare table for test

const mytableSQL = "CREATE TABLE IF NOT EXISTS `mytables` (" +
" `ID` int(11) NOT NULL," +
" `username` varchar(16) DEFAULT NULL," +
" `age` int(8) NOT NULL," +
" `phone` varchar(11) NOT NULL," +
" INDEX `idx_username` (`username`)" +
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;"

func prepare(db *gorm.DB) {
db.Exec(mytableSQL)
}
2 changes: 1 addition & 1 deletion examples/cmd/sync_table/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
)

func init() {
dal.DB = dal.ConnectDB(conf.MySQLDSN)
dal.DB = dal.ConnectDB(conf.MySQLDSN).Debug()

prepare(dal.DB) // prepare table for generate
}
Expand Down
2 changes: 1 addition & 1 deletion examples/cmd/ultimate/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (
)

func init() {
dal.DB = dal.ConnectDB(conf.MySQLDSN)
dal.DB = dal.ConnectDB(conf.MySQLDSN).Debug()

prepare(dal.DB) // prepare table for generate
}
Expand Down
2 changes: 1 addition & 1 deletion examples/conf/mysql.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
package conf

const MySQLDSN = "root:local_maridb_test@tcp(localhost:3306)/test?charset=utf8mb4&parseTime=True"
const MySQLDSN = "root:local_mysql_test@tcp(localhost:3306)/test?charset=utf8mb4&parseTime=True"

const SQLiteDBName = "gen_sqlite.db"
5 changes: 2 additions & 3 deletions examples/dal/mysql.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@ import (

var DB *gorm.DB

func ConnectDB(dsn string) *gorm.DB {
var db *gorm.DB
func ConnectDB(dsn string) (db *gorm.DB) {
var err error

if strings.HasSuffix(dsn, "sqlite.db") {
Expand All @@ -22,7 +21,7 @@ func ConnectDB(dsn string) *gorm.DB {
}

if err != nil {
panic(fmt.Errorf("connect db fail: %s", err))
panic(fmt.Errorf("connect db fail: %w", err))
}

return db
Expand Down
23 changes: 21 additions & 2 deletions generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"gorm.io/gorm"
"gorm.io/gorm/schema"

"gorm.io/gen/helper"
"gorm.io/gen/internal/check"
"gorm.io/gen/internal/model"
"gorm.io/gen/internal/parser"
Expand Down Expand Up @@ -109,7 +110,7 @@ func (g *Generator) GenerateModelAs(tableName string, modelName string, fieldOpt
for i, opt := range fieldOpts {
modelFieldOpts[i] = opt
}
s, err := check.GenBaseStructs(g.db, model.Conf{
s, err := check.GenBaseStruct(g.db, model.Conf{
ModelPkg: g.Config.ModelPkgPath,
TablePrefix: g.getTablePrefix(),
TableName: tableName,
Expand Down Expand Up @@ -154,7 +155,7 @@ func (g *Generator) getTablePrefix() string {
func (g *Generator) GenerateAllTable(opts ...FieldOpt) (tableModels []interface{}) {
tableList, err := g.db.Migrator().GetTables()
if err != nil {
panic(fmt.Sprintf("get all tables fail: %s", err))
panic(fmt.Errorf("get all tables fail: %w", err))
}

g.successInfo(fmt.Sprintf("find %d table from db: %s", len(tableList), tableList))
Expand All @@ -166,6 +167,24 @@ func (g *Generator) GenerateAllTable(opts ...FieldOpt) (tableModels []interface{
return tableModels
}

// GenerateModelFrom generate model from object
func (g *Generator) GenerateModelFrom(obj helper.Object) *check.BaseStruct {
s, err := check.GenBaseStructFromObject(obj, model.Conf{
ModelPkg: g.Config.ModelPkgPath,
ImportPkgPaths: g.importPkgPaths,
TableNameNS: g.tableNameNS,
ModelNameNS: g.modelNameNS,
FileNameNS: g.fileNameNS,
})
if err != nil {
panic(fmt.Errorf("generate struct from object fail: %w", err))
}
g.modelData[s.StructName] = s

g.successInfo(fmt.Sprintf("parse object %s", obj.StructName()))
return s
}

// ApplyBasic specify models which will implement basic method
func (g *Generator) ApplyBasic(models ...interface{}) {
g.ApplyInterface(func() {}, models...)
Expand Down
42 changes: 42 additions & 0 deletions helper/object.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package helper

import (
"errors"
"fmt"
)

type Object interface {
TableName() string
StructName() string
FileName() string
ImportPkgPaths() []string

Fields() []Field
}

type Field interface {
Name() string
Type() string

GORMTag() string
JSONTag() string
Tag() string

Comment() string
}

func CheckObject(obj Object) error {
if obj.StructName() == "" {
return errors.New("Object's StructName() cannot be empty")
}

for _, field := range obj.Fields() {
switch "" {
case field.Name():
return fmt.Errorf("Object %s's Field.Name() cannot be empty", obj.StructName())
case field.Type():
return fmt.Errorf("Object %s's Field.Type() cannot be empty", obj.StructName())
}
}
return nil
}
11 changes: 9 additions & 2 deletions internal/check/checkstruct.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ type BaseStruct struct {
GenBaseStruct bool // whether to generate db model
FileName string // generated file name
S string // the first letter(lower case)of simple Name
NewStructName string // new struct name
StructName string // origin struct name
NewStructName string // internal query struct name
StructName string // origin/model struct name
TableName string // table name in db server
StructInfo parser.Param
Fields []*model.Field
Expand Down Expand Up @@ -120,6 +120,13 @@ func (b *BaseStruct) Relations() (result []field.Relation) {
return result
}

func (b *BaseStruct) StructComment() string {
if b.TableName != "" {
return fmt.Sprintf(`mapped from table <%s>`, b.TableName)
}
return `mapped from object`
}

func GetStructNames(bases []*BaseStruct) (res []string) {
for _, base := range bases {
res = append(res, base.StructName)
Expand Down
Loading

0 comments on commit 2492f01

Please sign in to comment.